淺談C#中標(biāo)準(zhǔn)Dispose模式的實(shí)現(xiàn)
需要明確一下C#程序(或者說.NET)中的資源。簡單的說來,C#中的每一個(gè)類型都代表一種資源,而資源又分為兩類:
托管資源:由CLR管理分配和釋放的資源,即由CLR里new出來的對象;
非托管資源:不受CLR管理的對象,windows內(nèi)核對象,如文件、數(shù)據(jù)庫連接、套接字、COM對象等;
毫無例外地,如果我們的類型使用到了非托管資源,或者需要顯式釋放的托管資源,那么,就需要讓類型繼承接口IDisposable。這相當(dāng)于是告訴調(diào)用者,該類型是需要顯式釋放資源的,你需要調(diào)用我的Dispose方法。
不過,這一切并不這么簡單,一個(gè)標(biāo)準(zhǔn)的繼承了IDisposable接口的類型應(yīng)該像下面這樣去實(shí)現(xiàn)。這種實(shí)現(xiàn)我們稱之為Dispose模式:
- public class SampleClass : IDisposable
 - {
 - //演示創(chuàng)建一個(gè)非托管資源
 - private IntPtr nativeResource = Marshal.AllocHGlobal(100);
 - //演示創(chuàng)建一個(gè)托管資源
 - private AnotherResource managedResource = new AnotherResource();
 - private bool disposed = false;
 - /// <summary>
 - /// 實(shí)現(xiàn)IDisposable中的Dispose方法
 - /// </summary>
 - public void Dispose()
 - {
 - //必須為true
 - Dispose(true);
 - //通知垃圾回收機(jī)制不再調(diào)用終結(jié)器(析構(gòu)器)
 - GC.SuppressFinalize(this);
 - }
 - /// <summary>
 - /// 不是必要的,提供一個(gè)Close方法僅僅是為了更符合其他語言(如C++)的規(guī)范
 - /// </summary>
 - public void Close()
 - {
 - Dispose();
 - }
 - /// <summary>
 - /// 必須,以備程序員忘記了顯式調(diào)用Dispose方法
 - /// </summary>
 - ~SampleClass()
 - {
 - //必須為false
 - Dispose(false);
 - }
 - /// <summary>
 - /// 非密封類修飾用protected virtual
 - /// 密封類修飾用private
 - /// </summary>
 - /// <param name="disposing"></param>
 - protected virtual void Dispose(bool disposing)
 - {
 - if (disposed)
 - {
 - return;
 - }
 - if (disposing)
 - {
 - // 清理托管資源
 - if (managedResource != null)
 - {
 - managedResource.Dispose();
 - managedResource = null;
 - }
 - }
 - // 清理非托管資源
 - if (nativeResource != IntPtr.Zero)
 - {
 - Marshal.FreeHGlobal(nativeResource);
 - nativeResource = IntPtr.Zero;
 - }
 - //讓類型知道自己已經(jīng)被釋放
 - disposed = true;
 - }
 - public void SamplePublicMethod()
 - {
 - if (disposed)
 - {
 - throw new ObjectDisposedException("SampleClass", "SampleClass is disposed");
 - }
 - //省略
 - } }
 
在Dispose模式中,幾乎每一行都有特殊的含義。
在標(biāo)準(zhǔn)的Dispose模式中,我們注意到一個(gè)以~開頭的方法:
- /// <summary>
 - /// 必須,以備程序員忘記了顯式調(diào)用Dispose方法
 - /// </summary>
 - ~SampleClass()
 - {
 - //必須為false
 - Dispose(false);
 - }
 
這個(gè)方法叫做類型的終結(jié)器。提供終結(jié)器的全部意義在于:我們不能奢望類型的調(diào)用者肯定會主動調(diào)用Dispose方法,基于終結(jié)器會被垃圾回收器調(diào)用這個(gè)特點(diǎn),終結(jié)器被用做資源釋放的補(bǔ)救措施。
一個(gè)類型的Dispose方法應(yīng)該允許被多次調(diào)用而不拋異常。鑒于這個(gè)原因,類型內(nèi)部維護(hù)了一個(gè)私有的布爾型變量disposed:
- private bool disposed = false;
 
在實(shí)際處理代碼清理的方法中,加入了如下的判斷語句:
- if (disposed)
 - {
 - return;
 - }
 - //省略清理部分的代碼,并在方法的***為disposed賦值為true
 - disposed = true;
 
這意味著類型如果被清理過一次,則清理工作將不再進(jìn)行。
應(yīng)該注意到:在標(biāo)準(zhǔn)的Dispose模式中,真正實(shí)現(xiàn)IDisposable接口的Dispose方法,并沒有實(shí)際的清理工作,它實(shí)際調(diào)用的是下面這個(gè)帶布爾參數(shù)的受保護(hù)的虛方法:
- /// <summary>
 - /// 非密封類修飾用protected virtual
 - /// 密封類修飾用private
 - /// </summary>
 - /// <param name="disposing"></param>
 - protected virtual void Dispose(bool disposing)
 - {
 - //省略代碼
 - }
 
之所以提供這樣一個(gè)受保護(hù)的虛方法,是為了考慮到這個(gè)類型會被其他類繼承的情況。如果類型存在一個(gè)子類,子類也許會實(shí)現(xiàn)自己的Dispose模式。受保護(hù)的虛方法用來提醒子類必須在實(shí)現(xiàn)自己的清理方法的時(shí)候注意到父類的清理工作,即子類需要在自己的釋放方法中調(diào)用base.Dispose方法。 
還有,我們應(yīng)該已經(jīng)注意到了真正撰寫資源釋放代碼的那個(gè)虛方法是帶有一個(gè)布爾參數(shù)的。之所以提供這個(gè)參數(shù),是因?yàn)槲覀冊谫Y源釋放時(shí)要區(qū)別對待托管資源和非托管資源。
在供調(diào)用者調(diào)用的顯式釋放資源的無參Dispose方法中,調(diào)用參數(shù)是true:
- public void Dispose()
 - {
 - //必須為true
 - Dispose(true);
 - //其他省略
 - }
 
這表明,這個(gè)時(shí)候代碼要同時(shí)處理托管資源和非托管資源。
在供垃圾回收器調(diào)用的隱式清理資源的終結(jié)器中,調(diào)用參數(shù)是false:
- ~SampleClass()
 - {
 - //必須為false
 - Dispose(false);
 - }
 
這表明,隱式清理時(shí),只要處理非托管資源就可以了。
那么,為什么要區(qū)別對待托管資源和非托管資源。在認(rèn)真闡述這個(gè)問題之前,我們需要首先弄明白:托管資源需要手動清理嗎?不妨先將C#中的類型分為兩類,一類繼承了IDisposable接口,一類則沒有繼承。前者,我們暫時(shí)稱之為非普通類型,后者我們稱之為普通類型。非普通類型因?yàn)榘峭泄苜Y源,所以它需要繼承IDisposable接口,但是,這個(gè)包含非托管資源的類型本身,它是一個(gè)托管資源。所以說,托管資源需要手動清理嗎?這個(gè)問題的答案是:托管資源中的普通類型,不需要手動清理,而非普通類型,是需要手動清理的(即調(diào)用Dispose方法)。
Dispose模式設(shè)計(jì)的思路基于:如果調(diào)用者顯式調(diào)用了Dispose方法,那么類型就該按部就班為自己的所以資源全部釋放掉。如果調(diào)用者忘記調(diào)用Dispose方法,那么類型就假定自己的所有托管資源(哪怕是那些上段中闡述的非普通類型)全部交給垃圾回收器去回收,而不進(jìn)行手工清理。理解了這一點(diǎn),我們就理解了為什么Dispose方法中,虛方法傳入的參數(shù)是true,而終結(jié)器中,虛方法傳入的參數(shù)是true。
注意:我們提到了需要及時(shí)釋放資源,卻并沒有進(jìn)一步細(xì)說是否需要及時(shí)讓引用等于null這一點(diǎn)。有一些人認(rèn)為等于null可以幫助垃圾回收機(jī)制早點(diǎn)發(fā)現(xiàn)并標(biāo)識對象是垃圾。其他人則認(rèn)為這沒有任何幫助。下一篇我們再細(xì)說這一點(diǎn)。
原文鏈接:http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html
【編輯推薦】
【責(zé)任編輯:彭凡 TEL:(010)68476606】















 
 
 
 
 
 
 