【发布时间】:2011-03-11 23:19:45
【问题描述】:
上下文
我正在使用具有工作区概念的 Winforms 应用程序 (.NET 3.5 SP1),它可以包含 n 个面板。每个面板(源自Panel)都有视图:
所有面板都被添加到 Workspace 类的 this.Controls 集合中(它派生自 UltraTabPageControl,一个 Infragistics GUI 控件)。每个视图都添加到其父级的Controls 集合中。因此,当在 Workspace 上调用 Dispose 时,面板和视图会自动释放,这完全符合预期。
我们还有另一个概念,称为ViewManager。它跟踪工作区中的所有View 控件,并负责维护单个“主”视图。每当创建 View 时,它都会向此管理器注册自己。这会将View 添加到列表中,然后运行一些逻辑来确定新的“主”视图,然后在每个视图上调用Synchronize() 方法。
当前的设计是,每当调用View.Dispose() 时,它都会从ViewManager 中注销自己。这会从列表中删除 View,然后运行相应的逻辑来检查剩余视图中的新主节点。
转折
当我们关闭整个工作区时,有一个特殊的Panel 类型需要在其他面板之前关闭。所以我们在Dispose 方法中有如下代码:
protected override void Dispose(bool disposing)
{
var theSpecialPanel = GetSpecialPanel();
if (theSpecialPanel != null)
{
theSpecialPanel.Dispose();
}
base.Dispose(disposing);
}
如果我们取出该代码,那么其他面板可能会在theSpecialPanel 之前被丢弃。这会导致检查新主面板的逻辑运行,在每个 View 上调用 Synchronize(),包括这个特殊面板。这会引发
“InvalidComObjectException:无法使用已与其底层 RCW 分离的 COM 对象。”
问题
这种设计是代码味道吗?强制一个特定的对象在其他对象之前被释放是不是很奇怪?
【问题讨论】:
-
看起来确实很糟糕。如果客户端代码不调用 Dispose 会发生什么?是否会因此异常而使终结器线程崩溃?这是一个不可调试的例外。阅读:blogs.msdn.com/b/visualstudio/archive/2010/03/01/…
-
这将是我的下一个问题:) - 什么会导致这个异常?感谢指点;正是我想要的。
-
注意:您的 Dispose 代码没有遵循良好的做法 - 如果 disposing==false,您正在访问其他托管对象。如果您有自己的非标准析构函数实现,或者如果终结器调用 Dispose(false),则可能会造成混淆。见msdn.microsoft.com/en-us/library/aa720161(v=VS.71).aspx
-
@AlexeiLevenkov 我故意不遵循微软经常引用的 Dispose 模式。我们不会实现终结器,派生类也不会这样做,所以我们可以forgo much of that cruft。感谢您的提醒。
标签: c# winforms events dispose