取消订阅事件

本文关键字:事件 取消 | 更新日期: 2023-09-27 18:31:40

在WinForms中,我可以使用IDisposable实现来取消订阅表单事件(如:已激活,加载,上下文菜单更改等)以帮助垃圾回收吗?

<小时 />

在 MSDN 取消订阅

若要防止在引发事件时调用事件处理程序,请取消订阅该事件。为了防止资源泄漏,应在释放订阅者对象之前取消订阅事件。在取消订阅事件之前,作为发布对象中事件基础的多播委托具有对封装订阅服务器事件处理程序的委托的引用。只要发布对象包含该引用,垃圾回收就不会删除订阅者对象。

取消订阅事件

是的,你可以,但是,根据有多少事件,我会说这将属于微优化类别。

是的,你可以,但如果事件处理程序是在您自己的类中定义的,并且也在同一实例中定义,那么您不必取消订阅该事件,因为发布者和订阅者是同一个对象。因此,不会在引用中保留额外的对象。

如果订阅对象 A 以处理对象 B 的事件,则值得取消订阅对象 B 中的事件。否则,作为事件基础的多播委托将保存对这两个对象的引用。这将阻止垃圾回收器收集这两个对象。

为了补充@Maarten的答案,与其在Form内处理"您自己的"事件,不如覆盖调用这些事件的众多protected virtual方法中的任何一种通常要简单得多。

即,而不是附加到Load事件:

this.Load += DoStuff;
private void DoStuff(object sender, EventArgs e) 
{
    // do stuff
}

您应该简单地覆盖OnLoad方法,完全不需要考虑取消订阅:

protected override void OnLoad(EventArgs e)
{
    // do stuff
    ...
    // call the base method to fire the event 
    // for external listeners
    base.OnLoad(e);
}  

这为您提供了仅用于外部对象事件的处理程序,这些处理程序应在您使用完它们后分离。

这也是为什么始终为每个公共事件使用protected virtual OnXXXX方法是一种很好的做法:允许派生类触发事件并在执行之前添加其他处理逻辑。

在通常的 WinForms 用例中,发布者和订阅者粘在一起并同时被处置。对按钮 OnClick 事件的订阅通常是包含按钮的窗口类的方法。从内存中删除窗口而不删除按钮是没有意义的。

在这些情况下,您无需取消订阅(据我所知)。

只有当你的订阅者类在发布者之前被释放时,它才重要,例如另一个窗口对窗口的 OnLoad 做出反应。那么使用IDisposable将是一个好主意。