2016-04-21 68 views
2

我使用弱事件的時候,我不能確定地退訂(否則我寧願+=-=而不是弱事件):弱事件和GC

class SomeType 
{ 
    public SomeType(...) 
    { 
     // object doesn't know when it will be removed 
     WeakEventManager(SomeSource, EventArgs).AddHandler(someSourceInstance, 
      nameof(SomeSource.SomeEvent), (s, e) => { ... }); 
    } 
} 

這樣,如果對象被垃圾回收,那麼事件處理程序不會被調用。完善。

但是,如果對象是尚未收集垃圾(但沒有更多強引用),則仍會調用事件處理程序。

我的問題相當一般:使用弱事件應該怎麼做?我是否應該在使用弱事件時期望事件處理程序中的無效呼叫?或者我應該force GC以避免這種情況(確定性的「清理」)?還有別的嗎?

+1

你已經得到它 - 弱事件允許訂閱者被垃圾收集,如果他們只能通過事件處理程序保持活躍狀態​​。他們不是因爲訂閱者符合GC要求而擺脫事件處理程序,這是一個副作用。你可以稱之爲「無效呼叫」,但事實並非如此。所以簡短的回答是「是的,你應該期望這樣的電話」; 「你甚至在做什麼這是一個問題,你是否缺少可以使你的意圖明確的代碼」? –

+0

@JeroenMostert,由於缺少' - =',導致內存泄漏。這並不容易*(我沒有說*不可能*)提供取消訂閱,因此我試圖使用弱事件。他們完美地解決了內存泄漏問題,但又出現了另一個問題......因此我的問題。我應該如何表達「這個對象要求這種方法被稱爲」清晰? 'IDisposable'? – Sinatr

+1

'IDisposable'是一種方式,是的。這是C#用於確定性清理的主要機制。雖然技術上僅用於釋放非託管資源,但即使未涉及非託管資源,它也被廣泛用於確定性清除框架。它具有語言支持('使用')的好處,並明確地向開發者發出信號,表明應該明確發佈該對象。然而,甚至比「IDisposable」更好的是找出對象的所有權規則,並且讓所有者負責「關閉」對象,如果可能的話。 –

回答

3

您應該始終期待事件處理程序可能會在您註銷後調用,即使是「強」事件。這樣的通話沒有任何無效。

protected void OnMyEvent(object sender, EventArgs e) 
{ 
    var ev = MyEvent; 
    if (ev != null) ev(this, EventArgs.Empty); 
} 

如果委託是未註冊ev = MyEventev.Invoke之間,它仍然會收到儘早通知:當你在事件處理程序是如何執行的

的簡單的情況是顯而易見的。我提到併發編程很難嗎?

但在你的情況下,問題真的是「爲什麼不知道什麼時候取消註冊?」回答這個問題,你會得到你的解決方案。爲什麼要調用一個事件處理程序來定位一個不再被強引用的對象是非法操作?這不像是部分收集對象或任何東西 - 它只是沒有收集。

+0

沒有使用的模式。 'IDisposable'?要只取消訂閱?在'C++'中有你依賴的析構函數。我可以創建一個'CallMeWhenYouDontNeedInstance()'方法,但我不確定(也不能確保這樣的對象的用戶會調用它)。這是使用弱事件的主要觀點:避免這種複雜情況。 – Sinatr

+0

@Sinatr你不能依賴C++中的析構函數,只能依靠某個人調用你的清理方法,無論是「Dispose」(這可能是錯誤的,迂迴地說)或不。在幾乎所有情況下,對象都應該擁有明確的所有權,當所有者完成後,它應該處理它們擁有的對象。我明白你想要做什麼以及爲什麼,但總的來說,這種模式很難遵循,無論你添加多少黑客手段,都會出現錯誤(「請注意!我正在取消註冊一個事件終結!「)。如果您有選擇使用確定性清理,請執行此操作。 – Luaan

+0

如果事件源的壽命比對象長,那麼即使對象被*處置*(除非您先取消訂閱,然後纔有資格使用GC),確實可以獲得調用處理程序。我沒有想到我的正常事件處理程序也有機會有無效的狀態。 – Sinatr