2011-02-22 44 views
11

如果我有一個看起來像這樣的代碼:我是否必須取消訂閱局部變量的匿名事件處理程序?

public void Foo() 
{ 
    Bar bar = new Bar(); 

    bar.SomeEvent += (sender, e) => 
    { 
     //Do something here 
    }; 

    bar.DoSomeOtherThingAndRaiseSomeEvent(); 
} 

bar當方法用完的範圍,否則我將不得不手動從事件取消,以防止內存泄漏,因爲收集參考SomeEvent

回答

18

你的情況是好的;事件用戶不會阻止出版商被收集,但可能發生相反的。

例如,

class Foo 
{ 
    public event EventHandler FooEvent; 

    void LeakMemory() 
    { 
     Bar bar = new Bar(); 

     bar.AttachEvent(this); 
    } 
} 

class Bar 
{ 
    void AttachEvent(Foo foo) 
    { 
     foo.FooEvent += (sender, e) => { }; 
    } 
} 

在這種情況下,內BarLeakMemory創建的實例不能被收集,直到

  • 由拉姆達表示的匿名方法是從FooEvent'移除小號調用列表
  • foo的實例,其中它的連接可以被收集

這是因爲事件(這只是普通的delegate實例中的一些語法糖)會保存到調用時要調用的代理列表中,並且這些代理中的每個代理都會依次引用該對象它附加到(在這種情況下,Bar的實例)。

請注意,我們只談論集合資格這裏。僅僅因爲它符合(或者,甚至,真的,如果),它將被收集,只是它可以是。

+0

是否存在的「弱引用事件」以任何標準形式/成語? – 2011-02-22 18:21:25

1

好了,對象是指bar不會自動垃圾立即收集......它只是對bar變量不會防止它被垃圾收集。

的事件處理程序將不會阻止Bar實例被垃圾收集或者雖然 - 「正常」的問題,就是一個事件處理程序保持一個事件的用戶被垃圾收集(如果它使用一個實例方法或在匿名函數中捕獲「this」)。它通常不會影響垃圾收集的發佈商。只要記住發佈者需要保留對所有訂閱者的引用 - 訂閱者不需要記住它訂閱的內容,除非它明確地想要取消訂閱或稍後使用其他成員。

假設沒有別的保持你的Bar活着的情況下,你的代碼應該罰款。

1

上述答案是正確的;我只想做一個筆記。如果您保留對委託/ lambda的其他引用,那麼用作處理程序的匿名委託只能取消訂閱。這是因爲lambda表達式是「函數文本」,有點像字符串常量,但是不像串確定的平等時,他們沒有比較語義:

public event EventHandler MyEvent; 

... 

//adds a reference to this named method in the context of the current instance 
MyEvent += Foo; 

//Adds a reference to this anonymous function literal to MyEvent 
MyEvent += (s,e) => Bar(); 

... 

//The named method of the current instance will be the same reference 
//as the named method. 
MyEvent -= Foo; 

//HOWEVER, even though this lambda is semantically equal to the anonymous handler, 
//it is a different function literal and therefore a different reference, 
//which will not match the anonymous handler. 
MyEvent -= (s,e) => Bar(); 

var hasNoHandlers = MyEvent == null; //false 

//To successfully unsubscribe a lambda, you have to keep a reference handy: 

EventHandler myHandler = (s,e) => Bar(); 

MyEvent += myHandler; 

... 

//the variable holds the same reference we added to the event earlier, 
//so THIS call will remove the handler. 
MyEvent -= myHandler; 
相關問題