3

我注意到我们的 .NET WinForms 应用程序中的有趣行为。我们有一个添加了许多 mdi 子项的 mdi 表单。这些子窗体收听“广播”事件,该事件本质上是刷新自身的调用。该事件在基类中声明,并在子窗体中添加监听事件。

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

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

洞察力将不胜感激。

(使用 C#、.NET 3.5)

4

4 回答 4

4

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

至于为什么窗口在关闭时不分离所有事件处理程序。如果确实如此,那将是非常奇怪的行为。仅仅因为您关闭了一个窗口并不意味着您已经完成了它,您可以重新打开它,将其中的数据保存到持久状态。在窗口上调用 close 与调用任何其他方法相比没有特殊属性,它不会处理窗口,将其标记为收集或其他任何东西。

于 2009-07-15T13:33:16.487 回答
1

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

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

于 2009-07-15T13:34:00.357 回答
1

是的,这是设计的行为,这也是WeakEvent模式被构思出来的原因。

于 2009-07-15T14:34:59.800 回答
0

您的事件订阅“计入”作为对您的子表单的引用。(因此您的子表单也不会被垃圾收集)。

要查看发生了什么,请查看代表的帮助。它有一个名为 Target(对象类型)的成员,指向订阅者。所以,你仍然有一个引用链:

MDI父(事件发布者)->委托->您的子表单。

您必须在 Dispose() 中清理您的事件订阅,否则您的子表单将永远无法进行垃圾收集。

现在,如果您在网上寻找“弱引用事件”,您会发现人们发布的许多解决方法来定义弱事件。这里只是一个例子: http: //www.codeproject.com/KB/cs/weakeventhandlerfactory.aspx

我也必须制作一个原型,如果你想要的话,我很乐意分享。但是,我的建议是坚持常规事件并在 Dispose() 中进行清理。

于 2009-07-15T14:37:00.250 回答