有趣的事件“处置”行为

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

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

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

洞察力将非常感激。

(使用C#,.NET 3.5)

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

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

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

您应始终确保事件取消订阅,以避免此类泄漏。

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

您的活动订阅“计入”作为您的子表单的引用。 (所以你的孩子的表格也没有被垃圾收集)。

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

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

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

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

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