在 WinForms 中,我可以使用 IDisposable 实现来取消订阅表单事件(如:Activated、Load、ContextMenuChanged 等)来帮助垃圾收集吗?
在MSDN退订
要防止在引发事件时调用您的事件处理程序,请取消订阅该事件。为了防止资源泄漏,您应该在处置订阅者对象之前取消订阅事件。在您取消订阅某个事件之前,发布对象中该事件基础的多播委托具有对封装订阅者事件处理程序的委托的引用。只要发布对象持有该引用,垃圾回收就不会删除您的订阅者对象。
是的,您可以,但是,取决于有多少事件,我会说这将属于微优化类别。
是的,您可以,但是如果事件处理程序是在您自己的类中定义的,并且也在同一个实例中定义,那么您不必取消订阅该事件,因为发布者和订阅者是同一个对象。因此,没有额外的对象被引用。
如果您订阅对象 A 以处理对象 B 的事件,那么取消订阅对象 B 中的事件是值得的。否则,作为事件基础的多播委托将持有对这两个对象的引用。这将阻止垃圾收集器收集这两个对象。
要添加到@Maarten 的答案,而不是在 a 中处理“您自己的”事件,覆盖调用这些事件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 将是一个好主意。