【发布时间】:2010-03-28 13:19:18
【问题描述】:
有没有一种简单的方法来遍历订阅给定事件的所有处理程序?我的问题是客户订阅但忘记取消订阅,因此发生内存泄漏。我需要一种方法让对象在 Dispose 方法中断开其事件的所有处理程序,这样就不会发生泄漏 - 至少不是因为事件。
【问题讨论】:
标签: c# events memory-leaks
有没有一种简单的方法来遍历订阅给定事件的所有处理程序?我的问题是客户订阅但忘记取消订阅,因此发生内存泄漏。我需要一种方法让对象在 Dispose 方法中断开其事件的所有处理程序,这样就不会发生泄漏 - 至少不是因为事件。
【问题讨论】:
标签: c# events memory-leaks
为您的事件设置 null:MyEvent = null;
但最好让客户退订您的活动。
【讨论】:
MyEvent = new MyEvent()不是更好吗?那么当你再次订阅时,你不会得到空异常。
另一种方法是使用所谓的“弱委托”模式。当您使用此技术时,事件仅使用 WeakReference 引用客户端,这不会将它们保存在内存中。当客户端不再被应用程序的其他部分引用时,它们将被垃圾收集(并且处理程序也可以在客户端被收集时自动取消注册)。
这通常用于解决客户端“忘记”取消订阅 .NET 事件的问题,因此听起来这可能非常适合您的问题。
【讨论】:
仅当另一个对象(侦听器)在您的对象(事件源)之前死亡时才会发生内存泄漏。在这种情况下,事件源仍然保留对侦听器的引用,这会阻止侦听器被收集。当事件源死亡时,也可能会收集未订阅的侦听器。
如果事件源在侦听器之前死亡,这不会阻止侦听器稍后被收集,此时对它的所有其他引用都设置为 null。
这意味着,事件源的 Dispose 方法不是解决这个问题的正确地方。它只能在侦听器代码中解决。简单地说,除了要求客户编写干净的代码之外,您什么也做不了。
【讨论】:
在撰写本文时,最准确的答案是最不受欢迎的。
您可以取消事件处理程序,但是在它的所有者被 zapped 之后无论如何它都会被 zapped - 超级整洁并没有错,但就像 Alex 所说,这不是问题所在。
Adi 的源类将允许在收集它自己时收集监听对象,这是毫无疑问的。所以问题是 Adi 的源对象一直保持打开状态,可能来自他客户代码中的一些长引用链。
以下博客文章还介绍了 Adi 描述的解决方案,并解释了为什么它是不必要的。
【讨论】: