我需要在表单中取消订阅事件吗?

4
我正在尝试理解如何取消订阅控件事件。假设我有一个文本框,并使用WinForms设计器订阅了TextChanged事件。
在Textbox析构函数中,TextChanged事件是否会自动取消订阅?还是为了避免内存泄漏,我必须明确取消订阅?请注意保留HTML标记。
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 );
}
4个回答

6
在这种情况下,我认为不取消订阅是可以的,因为您订阅的TextBox完全包含在父控件中(或者这就是我所假设的)。因此,当不再存在对父控件的引用时,将不会有对TextBox的外部引用,因此两个对象都将变得适合进行GC。
有些情况下,您应该取消事件订阅以防止内存泄漏,因为事件持有的引用(在其订阅者列表中)与任何其他引用都相同,将阻止订阅者被GC。
这种情况可能发生在对象订阅外部对象上的事件(即不属于此对象的对象)。在这种情况下,只有在被订阅对象有资格进行GC后,订阅者才能成为可回收对象。

6
任何订阅长寿对象事件的对象都应该实现IDisposable,并在其Dispose时取消订阅这些事件。从概念上讲,没有理由为什么对象在被处理时不能取消订阅所有事件,因为这样做可以避免出现对象的事件比预期寿命更长的问题。不幸的是,.NET中的事件架构没有提供方便的机制来确保在对象被处理时清除事件,并且当一个对象被处理时,有代码退订一堆事件可能会使人难以确定哪些事件真正需要清除。

3
这些事件实际上是事件处理程序(函数委托)的列表。因此,当您编写以下代码时:
this.emailTextBox.TextChanged += emailTextBox_TextChanged;

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

这意味着当文本框被释放时,该列表也将被释放,因此您不需要在这种情况下取消订阅事件,并且您不会发生内存泄漏。

所以回答你的问题,实际上并没有在文本框析构函数中取消订阅事件,但您不需要显式地这样做。

唯一需要取消订阅的情况是当您不希望您的函数在执行期间处理该事件时,但我认为我从未真正需要这样做。


2
是的,您最好取消订阅。正如官方文档所述(这里)。
为了防止资源泄漏,在处理订阅对象之前应该先取消订阅事件。在取消订阅事件之前,发布对象中潜在的多路广播委托仍然会引用封装订阅者事件处理程序的委托。只要发布对象保持该引用,垃圾回收就不会删除您的订阅对象。

链接是俄语的。通过将url中的ru-ru更改为en-us,您将获得英文版本:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events#unsubscribing - mortb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接