我正在尝试了解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 );
}
答案 0 :(得分:5)
在这种情况下,我相信可以不取消订阅,因为您订阅的TextBox完全包含在父控件中(或者我正在假设。)
因此,当不存在对父控件的进一步引用时,将不会对TextBox进行任何外部引用,因此两个对象都将符合GC的条件。
在某些情况下,应该取消订阅事件以防止内存泄漏,因为事件持有的引用(在其订阅者列表中)与任何事件相同其他参考,将阻止订阅者GC。
当对象订阅外部对象上的事件(即不属于此对象)时,可能会发生这种情况。在这种情况下,订阅者只有在订阅对象符合GC条件后才有资格获得GC。< / p>
答案 1 :(得分:5)
订阅来自较长寿命对象的事件的任何对象都应实现IDisposable
,并且应该在Dispose
d时取消订阅这些事件。从概念上讲,没有理由为什么对象在处理时不应取消订阅所有事件,因为这样做可以避免在订阅事件的对象活得超过预期的情况下出现问题。不幸的是,.NET中的事件体系结构没有提供任何机制来方便地确保在处理对象时清理事件,并且在处理对象时让代码取消订阅一堆事件可能会使确保少数事件真正变得更加困难需要被清理的是那些。
答案 2 :(得分:2)
事件实际上是事件处理程序列表(函数委托)。所以当你写这个:
this.emailTextBox.TextChanged += emailTextBox_TextChanged;
您实际上是将代理emailTextBox_TextChanged
添加到与TextChanged
事件关联的现有代理列表中。
这意味着当文本框被处理时,此列表也将被处理掉,因此在这种情况下您不需要取消订阅事件,也不会有内存泄漏。
所以为了回答你的问题,事件并没有真正取消订阅文本框析构函数,但你不需要明确地做。
取消订阅唯一有用的情况是你不希望你的函数在执行期间再处理事件,但我认为我从来没有真正需要这样做。
答案 3 :(得分:2)
是的,您最好退订。如官方文档所述(here)
为了防止资源泄漏,应该在处理订阅者对象之前取消订阅事件。取消订阅事件之前,发布对象中作为事件基础的多播委托具有对封装了订户的事件处理程序的委托的引用。只要发布对象拥有该引用,垃圾回收就不会删除您的订阅者对象。
答案 4 :(得分:1)
不,我从来没有在.net winforms编程中取消订阅事件。 如果那是必要的,我想它会由设计师自动添加......