当事件触发并尝试在不再存在的对象中执行事件处理程序时会发生啥?

Posted

技术标签:

【中文标题】当事件触发并尝试在不再存在的对象中执行事件处理程序时会发生啥?【英文标题】:What happens when an event fires and tries to execute an event-handler in an object that no longer exists?当事件触发并尝试在不再存在的对象中执行事件处理程序时会发生什么? 【发布时间】:2009-05-29 06:58:30 【问题描述】:

在一个类 ClassA 中,我有一个计时器对象。在这个类中,我为计时器经过的事件注册了事件处理程序。在另一个类 ClassB 中,我有一个用于计时器已用事件的公共事件处理程序。所以我在 ClassA 中注册了 ClassB 的事件处理程序,如下所示:

myTimer.Elapsed += ClassBInstance.TimerElapsed

如果我要创建一个新的 ClassBInstance 实例,并且当 ClassB 的事件处理程序的前一个实例仍然与计时器的 Elapsed 事件相关联时触发计时器 elapsed 事件,会发生什么?

例如:

ClassB classBInstance = new ClassB();
myTimer.Elapsed += classBInstance.TimerElapsed

classBInstance = new ClassB();
myTimer.Elapsed += classBInstance.TimerElapsed

【问题讨论】:

【参考方案1】:

AFAIK,只要有注册的事件,ClassBInstance 就不会被垃圾回收,因为事件持有对它的引用。

您必须确保取消注册不再使用的实例的所有事件。

重要的是注册实例是 IDisposable 的情况,因为在释放实例时可能会触发该事件。在这种情况下,我发现让实例自行注册并在 Dispose 中取消注册是最简单的方法。

【讨论】:

确实如此。特别是,这就是静态事件如此危险的原因。如果您不虔诚地退订,则很容易使大量对象保持活动状态。至少实例事件与持有支持字段的实例一起消亡 - 但没有用于静态的 GC。 还值得一提的是,WPF 会通过 WeakEvents 来避免这种情况,恕我直言,这太复杂了,需要一些语言支持。【参考方案2】:

事件的实现使得只要您的发布者还活着,所有订阅者都会被发布者保持活着,即使您没有对这些订阅者有任何其他引用。

当然,这也意味着,如果您希望独立于发布者清理订阅者,则必须分离订阅者。

【讨论】:

【参考方案3】:

如果前一个实例仍然存在,并且新实例还连接了一个事件处理程序,则该事件将触发两个处理程序(一次一个)。重要的是要跟踪何时将事件处理程序附加到事件,并且在不再需要它们时也将它们分离。否则旧实例将继续存在于内存中,执行可能导致意外结果的事件处理程序。

【讨论】:

【参考方案4】:

你可以使用我的 WeakEventHandler,基于 WeakReference。由于它保持对事件监听器的弱引用,它不会强制监听器存活。

see this answer

【讨论】:

以上是关于当事件触发并尝试在不再存在的对象中执行事件处理程序时会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

JS事件 鼠标经过事件(onmouseover)鼠标经过事件,当鼠标移到一个对象上时,该对象就触发onmouseover事件,并执行onmouseover事件调用的程序。

jQuery的事件编程

在 C++ 中触发事件并在 C# 中处理它们

JS事件 失焦事件(onblur)onblur事件与onfocus是相对事件,当光标离开当前获得聚焦对象的时候,触发onblur事件,同时执行被调用的程序。

小程序事件详解

小程序基础14:事件