2011-03-11 30 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

相關問題