2016-09-26 66 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 –