1

这是一个 WinForm MDI 应用程序问题(.net framework 3.0)。它将在 C# 中进行描述。抱歉,它有点长,因为我试图让事情尽可能清楚。

我有一个 MDI 应用程序。在某些时候,我发现一个 MDI 子表单从未发布过。有一个菜单可以创建 MDI 子窗体并显示它。当 MDI 子窗体关闭时,它应该被销毁,并且它所占用的内存应该还给 .net。但令我惊讶的是,这不是真的。所有 MDI 子窗体实例都保存在内存中。这显然是“内存泄漏”。好吧,这不是 .net 中的真正泄漏。只是我认为封闭形式应该是死的,但不知何故,至少有一个来自外部世界的未知参考仍然与封闭形式联系在一起。

我在网上阅读了一些文章。有人说,当 MDI 子窗体关闭时,我应该取消所有事件处理程序的连接,否则某些事件处理程序可能会使我的窗体保持活动状态。有人说应该在关闭表单之前清理 DataBindings,否则 DataBindings 将添加对某些全局 Hashtable 的引用,从而使我的表单保持活动状态。

我的表格包含很多东西。许多事件处理程序和许多 DataBindings 和许多 BindingSources 以及一些包含用户控件和 HelpProvider 的可疑控件。我创建了一个大方法,从所有相关控件中取消连接所有事件处理程序,清除所有 DataBindings 和 DataSources。HelpProvider 和用户控件被小心处理。

最后,我发现,我不必清除 DataBindings 和 DataSources。事件处理程序肯定会导致问题。而MDI的表单结构也有所贡献。

在我的实验中,我发现,如果你创建一个 MDI 子窗体,即使你关闭它,内存中仍然会有一个实例。参考来自主窗体的 PropertyStore。这意味着,除非主窗体关闭(应用程序结束),否则内存中总会有一个 MDI 子窗体实例。好消息是,无论您打开和关闭子窗体多少次,都只会出现一个实例,而不是大的“泄漏”。

当涉及到事件处理程序时,事情变得更加棘手。我必须解决这个问题,我表单上的所有事件处理程序都是匿名事件处理程序。这是一个示例代码:

//On MDI child form's design code...

Button btnSave = new Button(); 

btnSave.Click += new System.EventHandler(btnSave_Click);

wherebtnSave_Click也是 MDI 子窗体中的一个方法。对于各种控件和各种类型的事件,上述情况总是如此。对我来说,这是一个双向循环引用。btnSave 通过事件处理程序保持对 MDI 子窗体的引用。MDI 子窗体保留 btnSave 实例的引用。再次对我来说,这种双向循环引用不应该对.net 的垃圾收集器造成任何问题。这意味着我不必在处理表单时显式取消连接事件:

btnSave.Click -= btnSave_Click;

但事实并非如此。对于某些事件处理程序,它们是安全的。忽略它们不会导致任何重复实例。对于其他一些事件处理程序,它们会导致一个实例保留在内存中(与MDI表单结构类似的效果,但这次是由挂起的事件处理程序引起的)。对于其他一些事件处理程序,它们将导致在内存中打开每个实例。我对这三种类型的事件处理程序之间的差异感到非常困惑。控件的创建方式相同,事件的附加方式相同。有什么区别?(不要告诉我是事件处理方法有所不同。)任何人都有这种有线场景的经验并且对我有答案吗?非常感谢。

所以现在,为了安全问题,我将不得不在处理表单时断开所有事件处理程序。这将是每个控件的一长串类似代码。是否有一种使用反射以递归方式从控件中删除事件的通用方法?性能问题呢?

这就是我的故事的结尾,我仍然在我的问题中间。如有任何帮助,我感谢您。

4

1 回答 1

0

只要在子窗体中声明了事件处理程序连接到的对象,您就不必在处置子窗体时删除事件处理程序,但是当事件处理程序连接到的对象在外部声明时子表单,您必须在处置子表单时删除事件处理程序。

如果这段代码被执行了多次(并且 btnSave 是一个没有在子窗体中声明的对象)

btnSave.Click += btnSave_Click;

此代码必须执行相同的次数

btnSave.Click -= btnSave_Click;


可能是您在应用程序的某个地方引用了子表单,必须在垃圾收集器删除子表单对象之前删除此引用。

于 2009-12-28T14:22:06.340 回答