簡體   English   中英

在引擎蓋下引用弱引用的C#事件是一個好主意嗎?

[英]Is it a good idea to implement a C# event with a weak reference under the hood?

我一直想知道是否值得使用類似下面的內容(粗略的概念證明代碼)來實現弱事件(它們是合適的):

class Foo {

    private WeakEvent<EventArgs> _explodedEvent = new WeakEvent<EventArgs>();

    public event WeakEvent<EventArgs>.EventHandler Exploded {
        add { _explodedEvent += value; }
        remove { _explodedEvent -= value; }
    }

    private void OnExploded() {
        _explodedEvent.Invoke(this, EventArgs.Empty);
    }

    public void Explode() {
        OnExploded();
    }

}

允許其他類使用更常規的C#語法訂閱和取消訂閱事件,同時實際上使用弱引用實現:

static void Main(string[] args) {
    var foo = new Foo();
    foo.Exploded += (sender, e) => Console.WriteLine("Exploded!");

    foo.Explode();
    foo.Explode();
    foo.Explode();

    Console.ReadKey();
}

WeakEvent<TEventArgs>輔助類的定義如下:

public class WeakEvent<TEventArgs> where TEventArgs : EventArgs {

    public delegate void EventHandler(object sender, TEventArgs e);

    private List<WeakReference> _handlers = new List<WeakReference>();

    public void Invoke(object sender, TEventArgs e) {
        foreach (var handler in _handlers)
            ((EventHandler)handler.Target).Invoke(sender, e);
    }

    public static WeakEvent<TEventArgs> operator + (WeakEvent<TEventArgs> e, EventHandler handler) {
        e._handlers.Add(new WeakReference(handler));
        return e;
    }

    public static WeakEvent<TEventArgs> operator - (WeakEvent<TEventArgs> e, EventHandler handler) {
        e._handlers.RemoveAll(x => (EventHandler)x.Target == handler);
        return e;
    }

}

這是一個好方法嗎? 這種方法有什么不良副作用嗎?

這是一個壞主意,因為:

  1. 您的程序開始變得不確定,因為副作用取決於GC的操作。
  2. GCHandles的性能成本。

查看鏈接的答案。 這是95%的重復但不足以結束我認為的問題。 我會引用最相關的部分:


還存在由弱引用引起的語義差異非確定性 如果您將() => LaunchMissiles()掛鈎到某個事件,您可能會發現有時會啟動導彈。 其他時候GC已經帶走了處理程序。 這可以通過引入另一個復雜程度的從屬句柄來解決。

我個人覺得很少有事件的強烈參考性質是一個問題。 通常,事件連接在具有相同或非常相似的生命周期的對象之間。 例如,您可以在ASP.NET中的HTTP請求的上下文中掛接所有您想要的事件,因為在請求結束時, 所有內容都有資格進行收集。 任何泄漏的大小都是有限的,而且壽命很短。

關於您的特定實現的一些評論:

  1. 在調用它之前檢查handler.Target的值是否為null ,這樣就不會嘗試使用已經處置的對象來執行它。

  2. C#具有關於如何使用事件的特殊訪問規則。 除非該代碼具有對事件的私有訪問權限,否則您不能執行a.Event1 = a.Event2 + SomeOtherMethod 但是,這對於代表來說是允許的。 您的實現更像是委托而不是事件。 這可能不是一個主要的問題,但需要考慮的事情。

  3. 您的operator方法應返回一個新對象,而不是修改第一個參數並返回它。 實現operator +允許使用如下語法: a = b + c ,但在您的實現中,您正在修改b !的狀態。 對於人們如何期望這些運營商的工作,這不是猶太人; 您需要返回一個新對象而不是修改現有對象。 (另外,因此你的實現不是線程安全的。從另一個線程調用operator +而另一個線程引發事件會引發異常,因為在foreach期間修改了集合。)

暫無
暫無

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

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