對象引用是如何工作的
假設你有一個隨機的對象。該對象是類類型(不是值類型),而不是IDisposable
。基本上這意味着以下內容:
// y is some object
var x = y;
現在,x不會複製y中的所有數據,只是簡單地對y的內容進行新的引用。簡單。
爲確保不會發生內存泄漏,GC會跟蹤所有對象並(定期)檢查哪些對象可以訪問。如果一個對象仍然可以訪問,它不會被刪除 - 如果不是,它將被刪除。
然後有非託管代碼
只要你堅持託管代碼,一切都很好。當你遇到非託管代碼(比如說:GDI +,它是許多System.Drawing內容的本地對象)時,你需要做額外的記錄來擺脫代碼。畢竟,.NET運行時不太瞭解非託管數據 - 它只知道有一個指針。因此,GC將清除指針,但不清除數據 - 這會導致內存泄漏。
因此,來自.NET的傢伙增加了IDisposable
。通過實現IDisposable,您可以實現額外(非託管)清理,例如釋放非託管內存,關閉文件,關閉套接字等。
現在,GC知道終結器,它是作爲Disposable模式的一部分實現的:https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx)。但是,您通常不希望等待GC運行來清理非託管資源。因此,當一個對象可以被清理並且具有非託管資源時,通常呼叫Dispose()
是個好主意。
與System.Drawing.Bitmap
一樣,它實現了IDisposable
。
在大多數情況下,您可以簡單地將IDisposable
包裝在using
語句中,該語句將在不錯的try/finally子句中爲您調用Dispose()。例如:
using (var myBitmap = new Bitmap(...))
{
// use myBitmap
}
// myBitmap including all resources are gone.
什麼資源位圖
@HansPassant指出,資源位圖生成每次你訪問一個位圖屬性時新Bitmap
。這基本上意味着位圖被複制並需要處理。
換句話說:
// Free the old bitmap if it exists:
if (this.imageButton.DefaultImage != null)
{
this.imageButton.DefaultImage.Dispose();
this.imageButton.DefaultImage = null;
}
// assign new imageButton.DefaultImage
所以,這解決了內存泄漏,但會給你很多是圍繞複製的數據。
如果你不想處理
這裏談到爲什麼我被從漢斯此言驚訝的部分:)基本上,你每次分配Bitmap
到一個按鈕,讓你不想一遍又一遍地複製數據 - 這沒什麼意義。
因此,你可能會得到的想法資源包裝成一個「靜態」的容器和根本不釋放它在所有:
static Bitmap myPic1 = Resource1.Pic1;
static Bitmap myPic2 = Resource1.Pic2;
...
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1;
}
else
{
this.imageButton.DefaultImage = myPic2;
}
這工作,但會給你,如果你在某個問題點決定也生成圖像。舉例說,我們改變這樣的代碼:
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1; // #1 don't dispose
}
else
{
Bitmap myPic3 = CreateFancyBitmap(); // #2 do dispose
this.imageButton.DefaultImage = myPic3;
}
現在,這裏的問題是與組合。 myPic1
是一個靜態對象,不應該丟棄。另一方面,myPic3
不是,應該處置。如果你打電話Dispose()
,你會在#1處得到一個令人討厭的異常,因爲數據不再存在。沒有適當的方法來區分這兩者。
資源設計人員在生成正確的代碼方面做得不好。 'Pic1'和'Pic2'屬性應該是方法。每使用一次該屬性它們都會返回一個* new *位圖,該位圖應該在使用後丟棄。所以在這個非常可怕的borken代碼片段中,如果不是null,你必須處理imageButton.DefaultImage。通常,沒有100%的保證,這個圖像不是來自其他地方,因此可能在其他地方使用。很難評論糟糕的代碼。 –
@HansPassant什麼?好吧,謝謝你的提高,改變一切。我會改變我的答案; Zorakh似乎並沒有明白你的意思。 – atlaste
嗨,謝謝你的回答,雖然我不太清楚你的意思是Pic1和Pic2是返回一個新的位圖實例的方法。我特別將它們作爲資源包含在我的項目中,以便每次都使用相同(且唯一)的副本,而無需在途中處理實例。如果我的答案是正確的,這裏就是這種情況,所以在這裏我不必擔心這一點(實際上我沒有觀察到內存問題)。 或者在我上面的示例中是這樣的,但是最好是像你說的那樣去做?在那種情況下:你能告訴我優點嗎? Thx! – Zorakh