簡體   English   中英

C# 事件在幕后如何工作?

[英]How do C# Events work behind the scenes?

我正在使用 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。

我不知道什么,我試圖做將幫助......但最終我想拿出在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
    }
}

考慮到這一點,我意識到我並不真正了解事件背后發生了什么 - 我想了解 :)

我已經在一篇文章中詳細描述了這一點,但這里是摘要,假設您對代表本身感到滿意:

  • 事件只是一個“add”方法和一個“remove”方法,就像屬性實際上只是一個“get”方法和一個“set”方法一樣。 (實際上,CLI 也允許使用“raise/fire”方法,但 C# 永遠不會生成此方法。)元數據通過對方法的引用來描述事件。
  • 當您聲明一個類似字段的事件(如您的 ElementAddedEvent)時,編譯器會生成方法和一個私有字段(與委托類型相同)。 在類中,當您引用 ElementAddedEvent 時,您指的是該字段。 在課堂之外,您指的是該領域。
  • 當任何人訂閱調用 add 方法的事件(使用 += 運算符)時。 當他們取消訂閱時(使用 -= 運算符)調用刪除。
  • 對於類似字段的事件,有一些同步,但除此之外,添加/刪除只需調用 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 的行為。

  • 如果您希望“無操作”處理程序訂閱您的事件,以避免無效檢查,您可以執行以下操作:

     public EventHandler<EventArgs> ElementAddedEvent = delegate {};

    delegate {}只是一個匿名方法,它不關心它的參數並且什么都不做。

如果還有什么不清楚的,請追問,我會盡力提供幫助!

在幕后,事件只是具有特殊調用約定的委托。 (例如,您不必在引發事件之前檢查無效性。)

在偽代碼中, Event.Invoke() 分解如下:

如果事件有偵聽器以任意順序在此線程上同步調用每個偵聽器。

由於事件是多播的,它們將有零個或多個偵聽器,保存在一個集合中。 CLR 將遍歷它們,以任意順序調用它們。

要記住的一個重要警告是,事件處理程序與引發事件的線程在同一線程中執行。將它們視為產生新線程是一種常見的心理錯誤。 他們不。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM