2012-11-26 261 views
3

我對正確實現IDisposable的方式有些嘮叨疑惑。請考慮以下情形......與IDisposable混淆

public class Foo : IDisposable {...} 

public class Bar : IDisposable { 

    private bool disposed = false; 
    private readonly Foo MyFoo; 

    public Bar() { 
     this.MyFoo = new Foo(); 
    } 
    public Bar(Foo foo) { 
     this.MyFoo = foo; 
    } 
    ~Bar() { 
     Dispose(false); 
    } 

    protected virtual void Dispose(bool disposing) { 
     if (!this.disposed) { 
      if (disposing) { 
       if (MyFoo != null) { 
        this.MyFoo.Dispose(); 
        this.MyFoo = null; 
       } 
      } 
      this.disposed = true; 
     } 
    } 

    public void Dispose() { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

我的問題是:

1)如果一個類創建了一個一次性的對象,它應該調用在自己的Dispose該對象的Dispose()方法()方法?

2)如果一個可丟棄的對象作爲引用傳遞給一個類,那麼如果該類仍然對該引用對象調用Dispose()方法,或者應該將它留給創建該對象的類的第一個類?

上述模式似乎出現了很多(特別是與DI),但我似乎無法找到正確的方式來構造這個的具體例子。

回答

1
~Bar() { 
    Dispose(false); 
} 

當你發現自己是這樣寫代碼,先深吸一口氣,問「我是不是真的需要一個終結?」這是非常罕見的,你需要一個,只有當你自己擁有一個非託管資源的所有權時才需要終結器。

第一次試金石測試是「終結者確實什麼?如果你遵循這個規範,那很明顯。它調用Dispose(false),並且只有當參數爲true時,代碼纔會執行​​某些操作。接下來是你不需要終結者。這是完全正常的,終結者是微軟擔心的事情。他們編寫了包裝非託管資源的.NET框架類。 FileStream,Socket等等。最重要的是,SafeHandle類用於封裝操作系統句柄。他們有自己的終結者,你不會自己重寫。

因此,沒有一個終結,代碼完全崩潰的簡單和正確執行,你只需要調用存儲自己所有可支配對象的Dispose()方法:

public class Bar : IDisposable { 
    private readonly Foo MyFoo; 
    public Bar() { 
     this.MyFoo = new Foo(); 
    } 
    public void Dispose() { 
     MyFoo.Dispose(); 
    } 
} 
+0

謝謝漢斯,非常有用。冒着濫用你的幫助的風險,你對在類中創建的一次性對象的處理有什麼看法,而不是那些作爲參考提供的對象 - 例如在類構造器中? – Neilski

+0

這是你需要自己解決的問題。誰成爲對象的「所有者」?在完成使用之前,您無法真正承擔創建Foo對象的其他代碼。當您使用它時,其他代碼也不會知道*。所以通常的做法是轉讓所有權,然後處置它。例如,也由.NET框架,StreamWriter(Stream)構造函數完成。 –

2

參閱MSDN優秀文章 Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework

1)如果一個類創建了一個一次性的對象,應該調用它自己的Dispose()方法是對象的Dispose()方法?

是的,應該的。否則,Dispose將被調用。但是,這將增加至少一代的物體壽命。這是由於類定義中的終結器。請參閱上面的文章鏈接。

2)如果一個可任意處理的對象作爲引用傳遞給一個類,那麼如果該類仍然對該引用對象調用Dispose()方法,或者它應該將它留給創建該對象的類第一名?

它是調用者(更具體地說創建實例的類)調用Dispose方法的責任。

+0

謝謝你,所以,在上面的例子中,Bar應該創建一個標誌,指示Foo是內部創建的還是作爲參考傳遞的?然後可以在虛擬Dispose()方法中使用該標誌來調用MyFoo.Dispose()或不調用。 – Neilski

+0

其實Foo。Dispose應該由Foo本身在內部實現。我的意思是國旗。在Bar.Dispose中,應該調用Foo.Dispose。重複處置的電話應該沒有問題。此外,因爲Bar創建MyFoo,所以MyFoo.Dispose只能在Bar中調用,而不能在其他任何類中調用。 – Tilak

+0

在某些情況下,接收實現「IDisposable」類型參數的對象將接受調用其上的Dispose的責任。 「IDisposable」的基本概念是「最後一個離開房間請關燈」。在任何時候,應該有可能確切地確定一個負責清理每個「IDisposable」的對象;該責任始於其創建者,但如果創作者爲了某些其他對象的利益而創建對象並且不再需要它自己,則可能會被移交。 – supercat