2016-09-26 72 views
1

我很清楚事件訂閱和取消訂閱的語法。是否有明確的方式來確定哪些事件訂閱導致內存泄漏,哪些不會?

myEvent += myEventHandler; // subscribe 
myEvent -= myEventHandler; // unsubscribe 

我喜歡(包含在一個塊個體經營)事件預訂的lambda語法,但如果我需要退訂(以避免內存泄漏)我不能使用此語法。

因此,我的問題是什麼樣的事件需要取消訂閱,哪些可以忽略由GC來照顧? 舉例來說,我在UWP應用程序中使用以下內容,是否需要取消訂閱?如果是這樣,爲什麼?

  • PointerMoved事件(Windows.UI.Xaml納秒)爲一個視圖頁面(在代碼隱藏)
  • 在相同的命名空間一個訂閱頁面的從另一個網頁的事件。帶有事件的源頁面保持爲菜單容器,而訂閱頁面在用戶控制下導航。
+1

我沒有標記它,但這似乎是[本文]的副本(http://stackoverflow.com/questions/4526829/why-and-how-to-avoid-event-handler-memory-泄漏)。如果發佈者的壽命比訂閱者長得多,那麼就會發生內存泄漏,因此,如果您的發佈者的範圍足以在所有訂閱者都不在範圍之內時消失,那麼您可以「讓GC處理取消訂閱」,正如Jon Skeet所說的在那個環節中,「通常我發現出版商和訂閱者的生活時間大致相同」。你說過,「但是我不能使用這個語法,如果我需要退訂」 - 爲什麼不呢? – Quantic

+0

@Quantic - 回答最後一個問題:使用lambda語法,我假設取消訂閱是不可能的,但我可能是錯的。如果您知道使用lambda語法取消訂閱事件的語法,那麼我很感激。 – user2921851

+0

感謝@Quantic的指針 - 是的,我可以看到一個副本。 – user2921851

回答

0

C#事件是該語言中思路最少的特性之一。我的建議是:從來沒有使用它們。如果您不小心添加了一個已經添加的事件處理程序,那麼默認的實現將實際添加處理程序兩次。這是一個很難檢測和排除故障的錯誤。另外,如果您不小心刪除了從未添加過的事件處理程序,則不會發生錯誤:它會自動失敗。從本質上講,它是在拖曳着你:它告訴你「是的,完成了!」,欺騙你相信你的三段論是正確的,而事實上它是錯誤的。這不是開發軟件的方法。我知道全世界有很多人開發這樣的軟件,他們不知怎麼做了,我只能說我爲他們感到難過。不要這樣對你自己。

實現您自己的觀察者/可觀察模式,聲明沒有添加任何內容兩次,沒有首先添加任何東西都不會被刪除。在可觀察對象的生命週期結束時,斷言所有觀察者列表都是空的,這意味着所有觀察者都傾向於註銷。是的,垃圾處理本來就是爲了讓我們免於擔心這樣的事情而做出的魔術子彈,但是,猜猜看是什麼,事實並非如此。在紙上審查微不足道的例子和學術練習時,一切看起來都很好,但只要事情開始變得有點複雜,讓事情由魔法來處理就再也行不通了。

一旦您發現某個觀察者永遠不會從可觀察對象中註銷,您需要找出觀察者被分配的位置和/或註冊位置。您可以通過獲取當前堆棧跟蹤並將其存儲在觀察器中來實現此目的,以便稍後發現觀察者處於活動狀態時,可以轉儲其堆棧跟蹤。獲取堆棧跟蹤的方法如下:StackTrace stackTrace = new StackTrace();請注意,這是非常慢的,(微軟只知道爲什麼),所以只有在實際需要的時候才使用它,也就是說,在排除你知道有錯誤的特定類時。一旦錯誤得到解決,請立即將其刪除。

+0

有趣的角度,並感謝您花時間。那麼你將如何實現事件訂閱,例如PointerMoved或類似的事件呢?我總是打開一個更好的選擇。 – user2921851

+0

所有事件處理程序都可以表示爲接受一個,兩個等參數的「操作」委託,假設最多有四個參數。所以,你需要做的就是編寫四個不同的通用事件管理器,每個不同數量的參數。如果你很聰明,你可以將他們所有的通用功能提取到一個通用的基類中。或者,如果您不介意使用某些「魔術」,則可以編寫一個通用事件管理器,將事件傳遞給任何接口:http://blog.michael.gr/2011/10/intertwine-normalizing-interface.html –