2016-12-14 53 views
0

我一直在嘗試增加對垃圾收集的理解,管理非託管資源以及關於內存管理的「正確設計原則」,因爲我有興趣進入「低層次」編程和這類事情。調用Dispose到底做了什麼?

我明白,你應該使用using塊或確保這些非託管資源都在事實上得到處置一些其他的解決辦法,但我不明白什麼是引擎蓋下發生。

我看這篇文章:Implementing a Dispose Method在MSDN上,並通過這個特殊的線路困惑:

爲了確保資源總是清理適當,Dispose方法應該是可調用多次,未拋出例外。

讓我們看看他們的Dispose模式提供的示例代碼:

class DerivedClass : BaseClass 
{ 
    // Flag: Has Dispose already been called? 
    bool disposed = false; 

    // Instantiate a SafeHandle instance. 
    SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true); 

    // Protected implementation of Dispose pattern. 
    protected override void Dispose(bool disposing) 
    { 
     if (disposed) 
     return; 

     if (disposing) 
     { 
     handle.Dispose(); 
     // Free any other managed objects here. 
     } 

     // Free any unmanaged objects here. 

     disposed = true; 

     // Call base class implementation. 
     base.Dispose(disposing); 
    } 
} 

相信什麼上面引述的文本基本上說的是,「我們增加了一個bool disposed屬性,以便我們能檢查是否是truereturn如果是這樣,如果是false那麼我們處理實際的資源。這樣我們就不會實際上處理多次的東西「

但這對我沒有意義。如果我們已經擺脫了一個對象,你怎麼能第二次打電話給Dispose

探討,我寫了包含以下3行控制檯應用程序:

var cmd = new SqlCommand(); 
cmd.Dispose(); 
cmd.Dispose(); 

這編譯和不執行的問題 - 這是有道理的,因爲從文章中引用的文字。但我不明白什麼實際上發生。

我設置了一個斷點並跨過每一行。在調用第一個Dispose之後,我期望Visual Studio中的Locals窗口告訴我cmdnull。接着這一系列的想法,我問自己:「你怎麼能在null上撥打Dispose?」顯然,你不能。那麼發生了什麼?爲什麼cmd在第一次被處理後仍然是SqlCommand對象?

enter image description here

什麼究竟Dispose做的,如果我佈置我的對象爲什麼它似乎仍然存在的所有意圖和目的是什麼?

爲什麼/以下編譯和運行沒有問題?

var cmd = new SqlCommand(); 
cmd.Dispose(); 
cmd.CommandText = "I figured this would throw an exception but it doesn't"; 
+1

有沒有「引擎蓋下」。沒有涉及的魔法,它只是一種像其他任何方法一樣的方法。編寫'Dispose'方法的人必須正確實現 - 調用所有合適的'CloseHandle'函數等。你能用其他方法將「this reference」改爲null嗎?當然不是。你爲什麼期望'Dispose'可以做到這一點?這只是一種方法。 – Luaan

+1

除了Luaan的解釋之外,另請參閱http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface以獲得更大的圖片。 –

+1

你沒有選擇最糟糕的例子,[它什麼都沒有用](https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,1101)。可能在.NET 1.0中做了非常不同的事情。但是,在你處理它之後,你肯定不應該繼續使用一個對象,但希望這很明顯。可能拋出一個ObjectDisposedException。或者,因爲會員沒有做任何「沉重的」事情,所以可能不會。有意識地錯誤並不是那麼有用。 –

回答

2

IDisposable接口是由一些消費類和所謂的 - 因爲已經暗隼說 - 當你封裝類的使用在使用塊。這樣可以更輕鬆地保持非託管資源的清潔。 但Dispose就像任何其他方法,不要與析構函數/終結器(這是你顯然預期它是混淆)

+0

「...而不是與析構函數/終結器混淆「〜我認爲這是這裏的問題。閱讀關於['Object.Finalize'](https://msdn.microsoft.com/en-us/library/system.object.finalize (v = vs.110).aspx)我想我認爲'Dispose'(通過它的一系列'base.Dispose()'調用)最終調用了'Finalize'或做了*某事*除了我可能明確的在我的'Dispose'重寫中定義,因此'Dispose'實際上只是語法糖的一部分,它就是'using'語句,僅此而已⋯⋯ – sab669

+0

是的,你去!:-) –

+1

@ sab669實際上,它實際上通常是反過來 - 終結器(根本不用用戶可調用)經常調用Dispose方法,只是爲了將所有的清理邏輯保存在一個地方。.NET中的終結器是最終的安全網絡,用於確保非託管即使出現了問題,資源也會被丟棄(並且您忘記了調用正確的Dispose方法),而且它們也被濫用於其他薄gs,但這是他們唯一設計的,並且在終結器中失去了很多保證,所以編寫安全的終結器並非完全無關緊要。即使'this'可以是'null' :) – Luaan

1

沒有什麼特別之處Dispose除了這樣一個事實:using聲明可以調用它。除此之外,它就像任何其他.NET函數一樣。它不會神奇地將任何變量設置爲null,如果再次使用該對象或者類似的東西,會導致引發異常。

同樣,IDisposable不是任何特殊的接口,除非using聲明中的對象必須實現它。你可以考慮使用的語句是這樣的:

// using(var a = somethingDisposable()) {otherCode();} 
var a = somethingDisposable(); 
try 
{ 
    otherCode(); 
} 
finally 
{ 
    if(a != null) 
     ((IDisposable)a).Dispose(); 
} 
+0

但是......這是什麼意思。 Dispose做什麼。它只是遞增對象的Generation,以便每當垃圾收集器運行時它會照顧這些項目? – sab669

+1

不,正如我所說的,它與垃圾回收器完全沒有任何交互作用。這只是另一種.NET方法。沒有什麼特別的,除了'using'語句的好語法。 –

+1

'Dispose'does,你告訴它做什麼。這只是一種方法。您通常使用它來以受控方式擺脫非託管資源(即,在使用塊內處理您的實例時)。 –

相關問題