2011-03-11 27 views
1

上下文对象需要按特定顺序处理:这是代码味道吗?

我正在与一个应用程序的Winforms(.NET 3.5 SP1),其具有工作空间的概念,它可以包含面板的n个工作。每个面板(来自Panel派生)具有视图:

 
    .-----------------------. 
    |Workspace    | 
    |.--------. .--------. | 
    ||Panel1 | |Panel2 | | 
    ||.-----. | |.-----. | | 
    |||View1| | ||View2| | | 
    ||'-----' | |'-----' | | 
    |'--------' '--------' | 
    '-----------------------' 

所有面板被添加到集合this.Controls工作区类(其从UltraTabPageControl,一个Infragistics的GUI控制导出)。每个视图都会添加到其父项的Controls集合中。所以,当在工作区上调用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对象无法使用。”

问题

这是设计是一个代码味道?强制执行一个特定的对象在别人之前被处置是否很奇怪?

+1

它肯定看起来不好。如果客户端代码不调用Dispose会发生什么?这是否会导致此异常终止器线程崩溃?这是一个不可违反的例外。阅读此:http://blogs.msdn.com/b/visualstudio/archive/2010/03/01/marshal-releasecomobject-considered-dangerous.aspx?wa=wsignin1.0 – 2011-03-12 01:33:09

+0

这将是我的下一个问题: ) - 什么会导致这个异常?感谢指针;正是我所期待的。 – bentsai 2011-03-12 03:34:23

+0

注意:您的Dispose代码不符合良好做法 - 如果处置== false,则您正在访问其他托管对象。如果您有自己的非标准析构函数实现,或者如果终结器调用Dispose(false),那么可能会引起混淆。请参阅http://msdn.microsoft.com/en-us/library/aa720161(v=VS.71).aspx – 2011-03-12 04:02:27

回答

1

在你的代码片段中,我没有看到你处置其他面板的位置。

如果您处理所有面板,我不认为在决定您希望的任何处置顺序方面存在任何问题。如果我正确地关注了你,你可以这样做:

foreach (panel in Panels.Where(p => p != theSpecialPanel)) 
{ 
    panel.Dispose(); 
} 
theSpecialPanel.Dispose(); 
+0

其他面板由于位于工作区的'Controls'集合中而被丢弃。我没有明确地处理其他面板。这是否改变了事情? [您的订单错误(我想首先处理特定面板),但在您的示例中您有正确的想法。] – bentsai 2011-03-11 23:32:22

+0

我明白了。所以循环出现在'theSpecialPanel.Dispose();'之后,并且它隐含在'base.Dispose(disposing);'调用中。它对我来说看起来仍然很好,语义相似。你可以要求在面板前放置一个视图,那么为什么你不能要求一个面板会被放置在另一个面板之前呢? – 2011-03-11 23:34:37

2

要求某些对象按特定顺序排列是完全合理的。请注意,终结者的一个主要限制是他们无法保证处理订单的任何内容。考虑一个Froboz9000Connection对象(用于管理与Froboz 9000计算机的连接),该对象又拥有一个用于实际通信的串行端口。在程序结束之前,必须发送一定的命令序列给远程机器。正确的事件顺序是Frobozz9000Connection对象的Dispose方法将发送必要的命令序列,然后处理SerialPort。如果SerialPort首先被丢弃,Frobozz9000Connection对象将无法发送正确的命令序列来通知远程机器不再需要其服务。

这个问题,顺便说一句,是另一个(很多)我不喜欢终结器的原因之一。虽然有时候终结者肯定会有用,但我认为在绝大多数情况下,确保Dispose正确使用是非常重要的。

2

SpecialPane是否需要明确处置?或者您是否只想确保拨打Synchronize()始终正确?我在这里假设你的View来自Control

public void Synchronize() 
{ 
    if (this.IsDisposed || this.Disposing) return; // or return a 'remove me' flag 
    ... 
    // sync 
} 

不知道其余的代码,我假设这应该工作得相当好,而不依赖于终结器。您可以让每个View负责知道他们是否可以执行特定操作,而无需表单容器来了解系统的实现细节。

我相信它也有助于维护。依靠处理的顺序可能会很棘手,如果有其他开发人员出现,他们可能完全错过。如果视图以其他方式处理,而不是从表单处理方法中调用,它也会起作用。

哦,如果View没有扩展Control,那么给View查看它的父级的引用,并检查父级控制的Disposal状态。

+0

我觉得你的建议一般都很好。我试图在视图中添加此检查,但在我的情况下,它不起作用。我有一个AxWindowsMediaPlayer对象,它在Dispose发生之前得到最终确定,导致InvalidComObjectException异常。在例外情况下,“IsDisposed”和“Disposing”都是错误的。我试图确定这是为什么发生。 – bentsai 2011-03-14 17:05:59

相关问题