2010-07-15 58 views
9

在我當前的項目中,我使用的類實現了以下ITransaction接口,如下所示。這是可以撤消的事務的通用接口。我也有一個TransactionSet類,用於嘗試多個事務或事務集,並最終可用於創建事務樹。可以從Dispose()中引發事件嗎?

ITransaction的某些實現會保留對對象實例或對象的臨時引用,以便在以後調用Undo()時使用。稍後可以確認成功的交易,之後不再允許Undo(),並且因此也不再需要臨時數據。目前我使用Dispose()作爲我的確認方法來清理任何臨時資源。

但是,現在我希望我的交易也能夠引發事件以通知其他類別發生了什麼。除非交易確認,否則我不希望事件觸發。因爲我不希望允許事務通過撤消多次激發事件,然後再次運行。

由於我使用Dispose()來確認事務是否還存在任何錯誤,因此也從這些事件中引發了這些事件?或者,在我的界面上單獨使用Confirm()方法會更好,除了清除臨時數據的Dispose()之外還會觸發事件?我不能想到任何我想確認的情況,但不能處理交易。然而,我不清楚我在Dispose()內應該做什麼和不該做什麼。

public enum TransactionStatus 
{ 
    NotRun, // the Transaction has not been run, or has been undoed back to the original state 
    Successful, ///the action has been run and was successful 
    Error //there was an attempt to run the action but it failed 
} 

/// <summary> 
/// Generic transaction interface 
/// </summary> 
public interface ITransaction 
{ 
    TransactionStatus Status { get; } 

    /// <summary> 
    /// Attempts the transaction returns true if successful, false if failed. 
    /// If failed it is expected that everything will be returned to the original state. 
    /// Does nothing if status is already Successful 
    /// </summary> 
    /// <returns></returns> 
    bool Go(); 

    /// <summary> 
    /// Reverts the transaction 
    /// Only does something if status is successful. 
    /// Should return status to NotRun 
    /// </summary> 
    void Undo(); 

    /// <summary> 
    /// A message describing the cause of the error if Status == Error 
    /// Otherwise equal String.Empty 
    /// </summary> 
    string ErrorMessage { get; } 
} 

回答

3

IDisposable只是一個運行時集成的設計模式,它以比終結更高效的方式來幫助清理對象。在處置方法中,你「不能」做得很少,但是你應該謹慎地做某些事情。

儘管IDisposable.Dispose()方法不是「真正的」析構函數或終結器,但如果其他對象在處理事件期間維護(或者甚至可能帶有)對處置對象的引用,則它可能會對對象的生命週期產生不利影響。如果你對如何實現這樣一個系統小心,你可以減輕可能的副作用。然而,重要的是要意識到這種實現提供的潛力,例如,惡意編碼器的攻擊面越大,例如保持事務對象無限期地活動。

+0

許多.net類都有一個「Disposing」事件,如果沒有這個事件將非常困難。如果一個對象將持有對其他地方可能使用或可能不會使用的IDisposable的引用(例如,一個持有BackgroundImage的控件),則可以使用Disposing事件來允許主對象的持有者在必要時清理嵌套對象。 – supercat 2011-03-15 17:52:59

4

處置不是一個特殊的方法 - 它不像一個構造函數或終結或任何東西 - 它只是一個有用的模式來通知消費者在使用它做了一個對象。沒有理由不能舉辦活動。

+0

+1。是的,'Dispose()'不是終結者;做任何你想在那裏。 – 2010-07-15 04:38:38

+6

+1。請注意,實現'IDisposable'的「推薦」模式具有'Dispose()'和'Dispose(bool)'方法。當'Dispose(false)'被調用時,方法*被*從終結器調用,並且事件*不能被引發。 – 2010-07-15 11:22:11

0

配置應該簡單清理。我將實現Confirm()和Rollback()方法,如果dispose被調用時沒有先調用它們,那麼應該至少記錄一個錯誤。

0

當然,您可以在Dispose方法中觸發任何事件。但是,如果您想要觸發事件以確認事務是否存在,我認爲您應該有單獨的方法來觸發事件。 Dispose()是清理內部資源或將內部實例作爲衆所周知的模式處理的方式。處置後,您的交易安裝應該不存在或不再使用。因此,您可以考慮使用單獨的方法來確認暫時不可用,並在交易中標記或狀態以表明該情況。

1

知道這個問題已經在4年前問過了,但不滿足於我的答案,我添加了一個將答案中討論的一些要點和評論與其他方面相結合的問題。

定稿: 作爲@jrista指出的那樣,讓我們​​清楚了IDisposable無關與GC或完成本身 - 它只是一個慣例,強烈推薦的做法。但是,使用Dispose pattern可以從終結器中調用Dispose方法(如@Stephen Cleary指出的)。在這種情況下,你絕對不應該提出任何事件,nor should it access other managed objects就此事。

拋開Dispose/Finalizer問題,因爲你的類不需要一個Finalizer,因爲它們不包裝非託管資源,不過還是有其他問題。

內存泄漏/ Liftetime匹配: 這是活動的被提及的問題,並可能適用於您的交易實現了。如果您的事件發佈者的壽命超過事件訂閱者的壽命,那麼如果該訂閱者沒有取消訂閱該事件,則可能會發生內存泄漏,因爲發佈者將繼續堅持。如果您的交易相當長時間,並且您訂購了許多短期對象,您應該考慮在這些對象中實施處置,然後取消訂閱交易。見最少意外Should I always disconnect event handlers in the Dispose method?

原理: 這是個好主意,「濫用」的處置提交事務?我會說不,儘管有先例。以Stream爲例。通常Stream.Dispose被實現來調用Flush並且因此將數據提交到底層介質。但是,請注意,我們有一個明確的Flush方法,所以您應該添加該方法。我發現「處置承諾」違反了最不讓人意外的原則,明確的方法更加清晰(如果這是您期望的默認行爲,您仍然可以從Dispose中調用此方法)。

事件級聯/無效對象狀態:我認爲這是在Dispose沒有引發事件的最強爭論。事件具有級聯的傾向(即一個事件觸發其他事件和代碼),如果你不小心,最終可能會導致一些代碼決定回調正在處理的對象是一個好主意並因此可能處於無效狀態。沒有樂趣去調試,特別是如果對象可能被多個線程訪問!儘管如此,還有像Component.Disposed這樣的先例。

我建議不要從Dispose方法引發事件。當你結束事件發佈者的生命週期時,它的所有訂閱者都相應地更新他們的狀態是否真的很重要?在大多數情況下,我發現我無論如何都擺脫了整個對象圖(即發佈者比訂閱者的壽命更長)。在某些情況下,您可能還希望主動抑制在處置過程中發生的任何故障狀況(例如關閉TCP連接時)。

相關問題