7

デリゲート、イベント、およびこれら 2 つの機能の .NET 実装に関するスタック オーバーフローに関する非常に良い質問をいくつか見てきました。特に、「C# イベントは舞台裏でどのように機能しますか?」という 1 つの質問は、いくつかの微妙な点を非常によく説明する優れた回答を生み出しました。

上記の質問に対する答えは、次の点を指摘しています。

フィールドのようなイベントを宣言すると、コンパイラはメソッドとプライベート フィールド (デリゲートと同じ型) を生成します。クラス内で ElementAddedEvent を参照するときは、フィールドを参照しています。クラスの外では、フィールドを参照しています

同じ質問 (「フィールドのようなイベント」)からリンクされた MSDN の記事には、次のものが追加されています。

イベントを発生させるという概念は、イベントによって表されるデリゲートを呼び出すこととまったく同じです。したがって、イベントを発生させるための特別な言語構造はありません。

さらに詳しく調べたいので、イベントとデリゲートがコンパイルされる IL を表示するために、テスト プロジェクトを作成しました。

public class TestClass
{
    public EventHandler handler;
    public event EventHandler FooEvent;

    public TestClass()
    { }
}

デリゲート フィールドhandlerとイベントFooEventはほぼ同じ IL コードにコンパイルされ、コンパイラによって生成されたFooEventフィールドへのアクセスをラップするメソッドがいくつか追加されることを期待していました。しかし、生成された IL は、私が期待したものとはまったく異なりました。

.class public auto ansi beforefieldinit TestClass
    extends [mscorlib]System.Object
{
    .event [mscorlib]System.EventHandler FooEvent
    {
        .addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler)
        .removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler)
    }

    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        // Constructor IL hidden
    }

    .field private class [mscorlib]System.EventHandler FooEvent
    .field public class [mscorlib]System.EventHandler handler
}

addイベントは、コンパイラによって生成されたおよびメソッドを持つデリゲートにすぎないためremove、IL でイベントがそれ以上のものとして扱われるとは思っていませんでした。ただし、 add メソッドと remove メソッドは、通常のメソッドとは 異なり.event、で始まるセクションで定義されています。.method

.event私の最終的な質問は次のとおりです。イベントがアクセサー メソッドを持つデリゲートとして単純に実装されている場合、 IL セクションを持つ意味は何ですか? .methodセクションを使用して、これなしで IL に実装できませんでしたか? と.event同等.methodですか?

4

3 に答える 3

7

それが驚くべきことかどうかはわかりません...プロパティとフィールドの同じものと比較してください(プロパティはイベントと同じ機能の前にあるため:アクセサーによるカプセル化):

.field public string Foo // public field
.property instance string Bar // public property
{
    .get instance string MyType::get_Bar()
    .set instance void MyType::set_Bar(string)
}

また、イベントはフィールドについて何も言及していません。アクセサ (追加/削除)のみを定義します。デリゲート バッカーは実装の詳細です。自動実装プロパティがフィールドをバッキング メンバーとして宣言するのと同じように、フィールドのようなイベントがフィールドをバッキング メンバーとして宣言するのは、たまたまです。他の実装も可能です (特にフォームなどでは非常に一般的です)。

その他の一般的な実装:

スパース イベント (コントロールなど) - EventHandlerList (または類似):

// only one instance field no matter how many events;
// very useful if we expect most events to be unsubscribed
private EventHandlerList events = new EventHandlerList();
protected EventHandlerList Events {
    get { return events; } // usually lazy
}

// this code repeated per event
private static readonly object FooEvent = new object();
public event EventHandler Foo
{
    add { Events.AddHandler(FooEvent, value); }
    remove { Events.RemoveHandler(FooEvent, value); }
}
protected virtual void OnFoo()
{
    EventHandler handler = Events[FooEvent] as EventHandler;
    if (handler != null) handler(this, EventArgs.Empty);
}

(上記はほぼ win-forms イベントのバックボーンです)

ファサード (これは「送信者」を少し混乱させますが、中間コードが役立つことがよくあります):

private Bar wrappedObject; // via ctor
public event EventHandler SomeEvent
{
    add { wrappedObject.SomeOtherEvent += value; }
    remove { wrappedObject.SomeOtherEvent -= value; }
}

(上記は、イベントの名前を効果的に変更するためにも使用できます)

于 2008-10-20T20:41:25.320 に答える
2

イベントはデリゲートと同じではありません。イベントは、イベントのハンドラーの追加/削除をカプセル化します。ハンドラーはデリゲートで表されます。

すべてのイベントに対して AddClickHandler/RemoveClickHandler などを記述することもできますが、それは比較的面倒であり、VS のようなツールがイベントを他のものから分離することは容易ではありません。

これはまさにプロパティと同じです。(Java で行うように) GetSize/SetSize などを記述できますが、プロパティを分離することで、構文上のショートカットが利用可能になり、ツールのサポートが向上します。

于 2008-10-20T20:41:34.870 に答える
1

add、remove、メソッドのペアであるイベントを持つポイントはカプセル化です。

ほとんどの場合、イベントはそのまま使用されますが、イベントに関連付けられたデリゲートをフィールドに格納したくない場合や、イベント メソッドの追加または削除で追加の処理を実行したい場合もあります。

たとえば、メモリ効率の高いイベントを実装する 1 つの方法は、プライベート フィールドではなくディクショナリにデリゲートを格納することです。これは、項目が追加されたときにのみディクショナリのサイズが大きくなるのに対し、フィールドは常に割り当てられるためです。このモデルは、Winforms と WPF がメモリを効率的に使用するために使用するものと似ています (winforms と WPF はキー付き辞書を使用して、リストではなくデリゲートを格納します)。

于 2008-10-20T20:55:07.680 に答える