[英]Is keeping track of c# GC objects (to avoid memory leaks) with a weak reference dictionary a good idea?
[英]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;
}
}
這是一個好方法嗎? 這種方法有什么不良副作用嗎?
這是一個壞主意,因為:
查看鏈接的答案。 這是95%的重復但不足以結束我認為的問題。 我會引用最相關的部分:
還存在由弱引用引起的語義差異和非確定性 。 如果您將() => LaunchMissiles()
掛鈎到某個事件,您可能會發現有時會啟動導彈。 其他時候GC已經帶走了處理程序。 這可以通過引入另一個復雜程度的從屬句柄來解決。
我個人覺得很少有事件的強烈參考性質是一個問題。 通常,事件連接在具有相同或非常相似的生命周期的對象之間。 例如,您可以在ASP.NET中的HTTP請求的上下文中掛接所有您想要的事件,因為在請求結束時, 所有內容都有資格進行收集。 任何泄漏的大小都是有限的,而且壽命很短。
關於您的特定實現的一些評論:
在調用它之前檢查handler.Target
的值是否為null
,這樣就不會嘗試使用已經處置的對象來執行它。
C#具有關於如何使用事件的特殊訪問規則。 除非該代碼具有對事件的私有訪問權限,否則您不能執行a.Event1 = a.Event2 + SomeOtherMethod
。 但是,這對於代表來說是允許的。 您的實現更像是委托而不是事件。 這可能不是一個主要的問題,但需要考慮的事情。
您的operator方法應返回一個新對象,而不是修改第一個參數並返回它。 實現operator +
允許使用如下語法: a = b + c
,但在您的實現中,您正在修改b
!的狀態。 對於人們如何期望這些運營商的工作,這不是猶太人; 您需要返回一個新對象而不是修改現有對象。 (另外,因此你的實現不是線程安全的。從另一個線程調用operator +而另一個線程引發事件會引發異常,因為在foreach期間修改了集合。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.