有趣的事件“处置”行为

时间:2009-07-15 13:30:09

标签: c# events dispose

我注意到.NET WinForms应用程序中有趣的行为。我们有一个mdi表单,添加了许多mdi子项。这些子表单会收听“广播”事件,这实际上是一次刷新自己的呼叫。事件在基类中声明,侦听事件在子表单中添加。

我注意到即使这些子窗体关闭,如果事件未在Dispose()方法中显式删除,事件仍然会被命中。

这背后的原因是什么?当然,如果表格已关闭,事件应该分开/处理?是因为实际事件本身是在外部类中声明的吗?这就是我的假设。

非常感谢Insight。

(使用C#,.NET 3.5)

4 个答案:

答案 0 :(得分:4)

该事件仍在范围内,因为它位于主窗体上,但仍然在子窗口中引用了该委托。因此,关闭窗口不会丢弃该对象,因为它仍然在此引用的范围内。这是在.NET中获得“内存泄漏”的一种非常常见的方式。还要考虑因为子窗口仍处于范围内,窗口内的所有内容仍在范围内,也不会被收集。

至于为什么窗口不会在关闭时分离所有事件处理程序。如果它这样做会是非常奇怪的行为。仅仅因为你关闭了一个窗口并不意味着你已经完成它,你可以重新打开它,将数据保存到持久状态。在窗口上调用close没有调用任何其他方法的特殊属性,它不会丢弃窗口,将其标记为集合或其他任何内容。

答案 1 :(得分:1)

你是对的。注册事件时,对表单的引用将添加到事件委托(在拥有该事件的对象中)。除非您删除注册,否则您的表单将永远不会被垃圾收集,因为它仍然至少有一个对它的引用(代理),并且在引发事件时仍然会发出调用。

你应该始终确保事件取消订阅,以避免这种泄漏。

答案 2 :(得分:1)

是的,这是设计行为,这也是WeakEvent模式得到满足的原因。

答案 3 :(得分:0)

您的活动订阅“计入”作为您的子表单的引用。 (所以你的孩子形式也不是垃圾收集)。

要查看正在发生的事情,请查找代理人的帮助。它有一个名为Target(类型为object)的成员,指向订阅者。所以,你仍然有一个活着的参考链:

MDI家长(活动发布者) - >代表 - >你的孩子形式。

您必须在Dispose()中清理您的活动订阅,否则您的子表单将永远不会有资格进行垃圾回收。

现在,如果您在网上浏览“弱参考事件”,您会发现人们发布了一些用于定义弱事件的变通方法。这只是一个例子:http://www.codeproject.com/KB/cs/weakeventhandlerfactory.aspx

我也不得不对原型进行原型设计,如果你愿意,我很乐意分享它。但是,我的建议是坚持定期事件并在Dispose()中清理。