老生常談的問題了,MSDN也有非常詳細的說明但看起來不是很系統。也曾經做過分析,但沒有總結下來又忘了,這次整理一下MSDN和網上搜集的一些資料,以備不時只需。
下面是MSDN對這兩個函數的建議使用方法
1 MSDN建議 2 // Design pattern for a base class. 3 public class Base : IDisposable 4 { 5 //保證重復釋放資源時系統異常 6 private bool _isDisposed = false; 7 8 // 析構函數,編譯器自動生成Finalize()函數由GC自動調用,保證資源被回收。 9 // 最好不要聲明空析構函數,造成性能問題 10 // 如果沒有引用非托管資源就不需要顯示聲明析構函數,會造成性能問題,系統會自動生成默認析構函數 11 ~Base() 12 { 13 // 此處只需要釋放非托管代碼即可,因為GC調用時該對象資源可能還不需要釋放 14 Dispose(false); 15 } 16 17 //外部手動調用或者在using中自動調用,同時釋放托管資源和非托管資源 18 public void Dispose() 19 { 20 Dispose(true); 21 GC.SuppressFinalize(this); ///告訴GC不需要再次調用 22 } 23 24 protected virtual void Dispose(bool disposing) 25 { 26 if (!_isDisposed) 27 { 28 if (disposing) 29 { 30 //釋放托管資源 31 } 32 // 釋放非托管資源 33 // 釋放大對象 34 35 this._isDisposed = true; 36 } 37 38 } 39 40 }
下面是通過Reflector工具對上面代碼反射出來的結果,可以看出析構函數直接被翻譯成Finalize()函數了,因為Finalize函數不能被重寫,所以只能用析構函數的方式實現Finalize方法。
1 Reflector反射結果 2 public class Base : IDisposable 3 { 4 // Fields 5 private bool _isDisposed; 6 7 // Methods 8 public Base(); 9 public void Dispose(); 10 protected virtual void Dispose(bool disposing); 11 protected override void Finalize(); 12 }
在.NET的對象中實際上有兩個用於釋放資源的函數:Dispose和Finalize。Finalize的目的是用於釋放非托管的資源,而Dispose是用於釋放所有資源,包括托管的和非托管的。
在這個模式中,void Dispose(bool disposing)函數通過一個disposing參數來區別當前是否是被Dispose()調用。如果是被Dispose()調用,那麼需要同時釋放 托管和非托管的資源。如果是被~Base()(也就是C#的Finalize())調用了,那麼只需要釋放非托管的資源即可。
這是因為,Dispose()函數是被其它代碼顯式調用並要求釋放資源的,而Finalize是被GC調用的。在GC調用的時候Base所引用的其它托管對象可能還不需要被銷毀,並且即使要銷毀,也會由GC來調用。因此在Finalize中只需要釋放非托管資源即可。另外一方面,由於在 Dispose()中已經釋放了托管和非托管的資源,因此在對象被GC回收時再次調用Finalize是沒有必要的,所以在Dispose()中調用 GC.SuppressFinalize(this)避免重復調用Finalize。
然而,即使重復調用Finalize和Dispose也是不存在問題的,因為有變量_isDisposed的存在,資源只會被釋放一次,多余的調用會被忽略過去。因此,上面的模式保證了:
1、 Finalize只釋放非托管資源;
2、 Dispose釋放托管和非托管資源;
3、 重復調用Finalize和Dispose是沒有問題的;
4、 Finalize和Dispose共享相同的資源釋放策略,因此他們之間也是沒有沖突的。
微軟對Dispose和Finalize方法使用准則
Finalize
下面的規則概括了 Finalize 方法的使用准則:
1、不能在結構中定義析構函數。只能對類使用析構函數。
2、一個類只能有一個析構函數。
3、無法繼承或重載析構函數。
4、無法調用析構函數。它們是被自動調用的。
5、析構函數既沒有修飾符,也沒有參數。
注意
基類的 Finalize 方法通過 C# 和 C++ 析構函數語法自動進行調用。
釋放
下面的規則概括了 Dispose 方法的使用准則:
下面是CSDN高手總結
1、Finalize方法(C#中是析構函數,以下稱析構函數)是用於釋放非托管資源的,而托管資源會由GC自動回收。所以,我們也可以這樣來區分 托管和非托管資源。所有會由GC自動回收的資源,就是托管的資源,而不能由GC自動回收的資源,就是非托管資源。在我們的類中直接使用非托管資源的情況很 少,所以基本上不用我們寫析構函數。
2、大部分的非托管資源會給系統帶來很多負面影響,例如數據庫連接不被釋放就可能導致連接池中的可用數據庫連接用盡。文件不關閉會導致其它進程無法讀寫這個文件等等。
實現模型:
1、由於大多數的非托管資源都要求可以手動釋放,所以,我們應該專門為釋放非托管資源公開一個方法。實現IDispose接口的Dispose方法是最好的模型,因為C#支持using語句快,可以在離開語句塊時自動調用Dispose方法。
2、雖然可以手動釋放非托管資源,我們仍然要在析構函數中釋放非托管資源,這樣才是安全的應用程序。否則如果因為程序員的疏忽忘記了手動釋放非托管資源, 那麼就會帶來災難性的後果。所以說在析構函數中釋放非托管資源,是一種補救的措施,至少對於大多數類來說是如此。
3、由於析構函數的調用將導致GC對對象回收的效率降低,所以如果已經完成了析構函數該干的事情(例如釋放非托管資源),就應當使用SuppressFinalize方法告訴GC不需要再執行某個對象的析構函數。
4、析構函數中只能釋放非托管資源而不能對任何托管的對象/資源進行操作。因為你無法預測析構函數的運行時機,所以,當析構函數被執行的時候,也許你進行操作的托管資源已經被釋放了。這樣將導致嚴重的後果。
5、(這是一個規則)如果一個類擁有一個實現了IDispose接口類型的成員,並創建(注意是創建,而不是接收,必須是由類自己創建)它的實例對象,則 這個類也應該實現IDispose接口,並在Dispose方法中調用所有實現了IDispose接口的成員的Dispose方法。
只有這樣的才能保證所有實現了IDispose接口的類的對象的Dispose方法能夠被調用到,確保可以手動釋放任何需要釋放的資源。
————————————————————————————————————————
一個人的時候,總是在想
我的生活到底在期待什麼……