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