我需要处理Dispose来注销事件吗?

5

假设我有两个类,它们都不是GUI组件。A类是一个短暂的对象,它注册了一个由长期存在的B对象声明的事件。例如:

public A(B b)
{
   b.ChangeEvent += OnChangeEvent;
}

如果A从未取消注册B的事件,那么A永远不会被垃圾回收吗?A需要一个Dispose方法来取消注册B的事件吗?
还有一个相关的第二个问题。如果A和B都应该在应用程序的整个执行时间内存在,那么A需要取消注册吗?
3个回答

4

关于第一个问题:是的,B有一个对A的引用。这样A就会和B一样长寿。如果你在UI应用程序中像注册App.OnIdle事件一样注册了事件,那么这是一种很好的释放内存的方式。

至于第二个问题:最终所有东西都会被销毁。


1
适当的断开事件的位置,正如您所怀疑的那样,应该在A的IDisposable.Dispose方法中。实际上没有什么好的替代方案。试图在A的终结器/析构函数中断开事件是无用的,因为在A从B中断开之前,A成为垃圾收集的可回收对象的唯一方式是B本身变得可回收;一旦发生了这种情况,订阅将成为一个无关紧要的问题,因此清理它是否被清理都无关紧要。

如果需要在长期发布者的生命周期内清理已弃用的短寿订阅者的事件,则可以通过如果发布和订阅类合作的方式来实现。例如,事件订阅者的公共接口可以是一个包装器对象,该对象持有对实际事件订阅者的引用,而订阅者本身不持有对该公共对象的强引用。在这种情况下,事件订阅将不会阻止公共对象从作用域中消失并成为可供终结的对象。公共对象中的终结器可以通知订阅对象不再需要订阅,并且应该取消订阅。由于这样的通知是异步到达的,因此发布订阅的对象必须提供一种方法,以便订阅者可以异步取消订阅。这可以通过普通的“取消订阅”请求来完成,前提是发布事件保证它是非阻塞和线程安全的;不幸的是,大多数事件都没有这样的保证。


0
如果B持有对A的引用,只有当B也符合垃圾回收条件时,A才不会被垃圾回收。
你不需要为此编写Dispose方法。一旦B没有任何指向它的引用,垃圾回收器就会聪明地处理B和A的释放。
如果它们在应用程序的生命周期内都是活动的,您不需要注销事件。

除非A也符合垃圾回收的条件。 - Jon Skeet
@Sean,我把B和A搞反了。一旦从B中移除外部引用,A也将被处理掉。 - kemiller2002
如果在 B 的生命周期内可以创建无限数量的 A,则未能断开事件将导致无限制的内存泄漏。如果 A 在其有用寿命期间需要 B 发布的事件,则 A 应该实现 IDisposable 并在其 Dispose 方法中断开与 B 的事件连接。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接