我是否必须在终结器中删除事件处理程序

本文关键字:删除 事件处理 程序 是否 | 更新日期: 2023-09-27 18:31:21

我有一个模型类,它有一个Saved事件。这个想法是,如果两个视图模型使用该模型对象,如果其中一个更改它,另一个将被更新。

当我不再使用事件处理程序的视图模型时,是否必须删除该处理程序?这是我的代码:

protected AbstractEntityViewModel(AbstractEntity ae)
{
    this.ae = ae;
    ae.Saved += delegate(object o, EventArgs e) 
    { 
        base.OnPropertyChanged(null);
    };
}

这是可以的,还是我需要更改它,以便在不再使用视图模型时-=摆脱委托?

我是否必须在终结器中删除事件处理程序

事件方向 引用

要知道是否确实需要分离事件处理程序,首先必须了解:

ae.Saved += delegate(object o, EventArgs e) 
{ 
    base.OnPropertyChanged(null);
};

表示ae现在引用this 。因此,具有事件的对象引用具有事件处理程序的对象。这不是相反的方式(事件处理程序引用事件)。

可以收集 GC 根未引用的对象

此外,虽然理想情况下要进行垃圾回收的对象不会被任何其他对象引用,但这并不是绝对必要的:

垃圾回收器可以以任何方式(路径)收集 GC 根未引用的所有对象。这意味着如果你有一个孤立的对象图(引用图的其他对象的对象,但图外没有对象引用图内的对象[也没有GC根]),那么整个对象图最终将被垃圾回收。图表越交织,GC 收集它的成本就越高。分离事件处理程序有助于更快地溶解此类图形。

正确清理对象

.Net 没有析构函数功能。相反,IDisposable模式和终结器(请参阅实现终结和释放以清理非托管资源)。

长话短说:

  • 当对象管理非托管资源时,需要终结方法(~Foo () { }用于class Foo)。当 GC 收集对象时,垃圾回收器会调用它们。所以确切的时刻不取决于你。
  • 一次性模式可用于清理托管和非托管资源。如果对象具有非托管资源,则它仍必须具有终结器。为什么?Dispose()方法由您调用。不能保证它正在执行。如果应用程序无法调用 Dispose() 方法,则仍有调用 finalize 的 GC。因此,基本上在Dispose中进行非托管资源清理只是一种性能改进(在某些情况下,这可能非常重要,除非您想在计算机中再插入几千兆字节的RAM......)。

如果您打算使用终结器,我绝对建议您阅读其中的文档,因为有很多内容我没有在这里介绍。请参阅实现最终确定和释放以清理非托管资源

您的示例

回到你的例子,在你构造一个AbstractEntityViewModel之后,只要你传递给AbstractEntityViewModelAbstractEntity保持活动状态,它就会保持活动状态,反之亦然。但是,当这两个都没有被 GC 根引用时,它们都会被垃圾回收。

如果分离事件处理程序,则即使AbstractEntity不能,也可以对AbstractEntityViewModel(或者更确切地说是具体的子类实例)进行垃圾回收。

另请参阅:了解 .NET 中的垃圾回收


长短是肯定的。如果 AbstractEntity 对象仍有对它的引用,则无法释放辅助对象。如果对象有可能被释放并且事件仍然存在(这也适用于静态事件),那么您需要手动删除事件处理程序,否则对象将不会释放。

事件可以作为原始观察者实现:主题为每个订阅的观察者保留一个处理程序,这意味着它们无法被垃圾回收。若要允许对观察者进行垃圾回收,必须将其作为观察者从主体中删除。

不需要

手动删除事件处理程序的唯一情况是当主体和观察者是同一实例时,因为垃圾回收器将检测循环引用并随后完成对象。