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