我是否需要取消订阅表单中的事件?

本文关键字:表单 事件 是否 取消 | 更新日期: 2023-09-27 18:18:18

我想了解Control事件是如何取消订阅的。假设我有一个文本框,并且我已经使用WinForms设计器订阅了TextChanged事件。

TextChanged事件Textbox析构函数中自动取消订阅,还是必须显式取消订阅以避免内存泄漏?

public void InitializeComponents()
{
    ...
    this.emailTextBox.TextChanged += emailTextBox_TextChanged;
    ...
}
public override void Dispose()
{
    if( disposing )
    {
        // DO I REALLY NEED THIS LINE?
        this.emailTextBox.TextChanged -= emailTextBox_TextChanged;
        if(components != null)
        {
            components.Dispose();
        }
    }
    base.Dispose( disposing );
}

我是否需要取消订阅表单中的事件?

任何从较长生存期的对象订阅事件的对象都应该实现IDisposable,并且当它是Dispose d时应该取消订阅这些事件。从概念上讲,对象在被处置时没有理由不取消订阅所有事件,因为这样做可以避免如果一个对象的事件被订阅的时间比预期的要长时出现问题。不幸的是,. net中的事件体系结构没有提供机制来方便地确保在对象被处置时事件被清除,并且在对象被处置时让代码取消订阅一堆事件可能会使确保真正需要被清除的少数事件在那些需要被清除的事件中变得更加困难。

在这种情况下,我认为不取消订阅是可以的,因为您订阅的文本框完全包含在父控件中(或者这是我的假设)

因此,当不存在对父控件的进一步引用时,将不会有任何对TextBox的外部引用,因此两个对象都将有资格进行GC。

在某些情况下,应该取消订阅事件以防止内存泄漏,因为事件(在其订阅者列表中)持有的引用与任何其他引用完全相同,并且可以防止订阅者被GC。

当对象订阅外部对象(即不属于该对象)上的事件时,就会发生这种情况。在这种情况下,订阅者只有在订阅对象有资格进行GC之后才有资格进行GC。

事件实际上是事件处理程序(函数委托)的列表。所以当你写这个时:

this.emailTextBox.TextChanged += emailTextBox_TextChanged;

您实际上将您的委托emailTextBox_TextChanged添加到与TextChanged事件相关的现有委托列表中。

这意味着当文本框被丢弃时,这个列表也将被丢弃,所以在这种情况下你不需要取消订阅事件,并且你不会有内存泄漏。

那么回答你的问题,事件并没有在文本框析构函数中真正取消订阅,但你不需要显式地这样做。

取消订阅唯一有用的情况是当你不希望你的函数在执行过程中再处理事件时,但我认为我从来没有真正需要这样做。

是的,你最好退订。正如官方文件所说(在这里)

为了防止资源泄漏,应该在处置订阅者对象之前取消对事件的订阅。在取消订阅事件之前,作为发布对象中事件基础的多播委托对封装订阅者事件处理程序的委托具有引用。只要发布对象持有该引用,垃圾收集就不会删除订阅对象。