C#イベントは舞台裏でどのように機能しますか?
質問
C#、. NET 3.5を使用しています。イベントを利用する方法、クラスでイベントを宣言する方法、他の場所からイベントをフックする方法などを理解しています。不自然な例:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
ただし、私が理解していないのは、 理解していないのは、イベントハンドラを宣言したときです
public EventHandler<EventArgs> ElementAddedEvent;
初期化されない-ElementAddedEventとは正確には何ですか?何を指しているのですか? EventHandlerは初期化されないため、以下は機能しません。
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
EventHandler.CreateDelegate(...)があることに気付きますが、すべてのメソッドシグネチャは、これが典型的なElementAddedEvent + = new EventHandler(MyMethod)を介して既存のEventHandlerにデリゲートをアタッチするためにのみ使用されることを示唆しています。 p>
やろうとしている what が役立つかどうかはわかりませんが、最終的には、LINQで抽象型の親DataContextを考え出します。彼らは「観察」したいそのため、BeforeUpdateやAfterUpdateなどのイベントを持つことができますが、タイプに固有です。このようなもの:
public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
このことを考えると、イベントで何が起こっているのか本当に理解していないことに気付きました。
解決
記事にこれをかなり詳細に書きました。が、デリゲート自体にかなり満足していると仮定した場合の要約です。 :
- イベントは単なる「追加」ですメソッドと「削除」メソッドは、プロパティが実際には単なる「取得」であるのと同じようにメソッドと&quot; set&quot;方法。 (実際、CLIでは「raise / fire」メソッドも使用できますが、C#はこれを生成しません。)メタデータは、メソッドへの参照を使用してイベントを記述します。
- フィールドのようなイベントを宣言するとき (ElementAddedEventと同様)コンパイラは、メソッドおよびプライベートフィールド(デリゲートと同じ型)を生成します。クラス内でElementAddedEventを参照するときは、フィールドを参照しています。クラス外では、フィールドを参照しています。
- addメソッドを呼び出すイベント(+ =演算子を使用)をサブスクライブする場合。削除を呼び出す(-=演算子を使用して)サブスクライブを解除するとき。
-
フィールドのようなイベントの場合、同期は行われますが、それ以外の場合は、add / removeはDelegateを呼び出します。結合 / 削除をクリックして、自動生成フィールドの値を変更します。これらの操作は両方ともバッキングフィールドに割り当てられます。デリゲートは不変であることに注意してください。つまり、自動生成されたコードは次のようになります。
// Backing field // The underscores just make it simpler to see what's going on here. // In the rest of your source code for this class, if you refer to // ElementAddedEvent, you're really referring to this field. private EventHandler<EventArgs> __ElementAddedEvent; // Actual event public EventHandler<EventArgs> ElementAddedEvent { add { lock(this) { // Equivalent to __ElementAddedEvent += value; __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); } } remove { lock(this) { // Equivalent to __ElementAddedEvent -= value; __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); } } }
-
あなたのケースで生成されたフィールドの初期値は
null
であり、すべてのサブスクライバが削除されると、常にnull
になります。 Delegate.Removeの動作。 -
「no-op」が必要な場合イベントをサブスクライブするハンドラー。無効チェックを回避するために、次のことができます。
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
delegate {}
は、パラメータを気にせず、何もしない、単なる匿名メソッドです。
まだ不明な点がある場合は、お問い合わせください。お手伝いします!
他のヒント
内部では、イベントは特別な呼び出し規約を持つデリゲートにすぎません。 (たとえば、イベントを発生させる前にnullityをチェックする必要はありません。)
擬似コードでは、Event.Invoke()は次のように分類されます:
イベントにリスナーがある場合 このスレッドで各リスナーを任意の順序で同期的に呼び出します。
イベントはマルチキャストであるため、0個以上のリスナーがコレクションに保持されます。 CLRはそれらをループし、それぞれを任意の順序で呼び出します。
覚えておくべき大きな注意点の1つは、イベントハンドラーは、イベントが発生したのと同じスレッドで実行されるということです。新しいスレッドを生成するものと考えるのはよくある精神エラーです。ありません。