需要明確一下C#程式(或者說.NET)中的資源。簡單的說來,C#中的每一個類型都代表一種資源,而資源又分為兩類:
託管資源:由CLR管理分配和釋放的資源,即由CLR裡new出來的對象;
非託管資源:不受CLR管理的對象,windows核心對象,如檔案、資料庫連接、通訊端、COM對象等;
毫無例外地,如果我們的類型使用到了非託管資源,或者需要顯式釋放的託管資源,那麼,就需要讓類型繼承介面IDisposable。這相當於是告訴調用者,該類型是需要顯式釋放資源的,你需要調用我的Dispose方法。
不過,這一切並不這麼簡單,一個標準的繼承了IDisposable介面的類型應該像下面這樣去實現。這種實現我們稱之為Dispose模式: public class SampleClass : IDisposable
{
// 示範建立一個非託管資源
private IntPtr nativeResource = Marshal.AllocHGlobal( 100 );
// 示範建立一個託管資源
private AnotherResource managedResource = new AnotherResource();
private bool disposed = false ;
/// <summary>
/// 實現IDisposable中的Dispose方法
/// </summary>
public void Dispose()
{
// 必須為true
Dispose( true );
// 通知記憶體回收機制不再調用終結器(析構器)
GC.SuppressFinalize( this );
}
/// <summary>
/// 不是必要的,提供一個Close方法僅僅是為了更符合其他語言(如C++)的規範
/// </summary>
public void Close()
{
Dispose();
}
/// <summary>
/// 必須,以備程式員忘記了顯式調用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;
}
// 讓類型知道自己已經被釋放
disposed = true ;
}
public void SamplePublicMethod()
{
if (disposed)
{
throw new ObjectDisposedException( " SampleClass " , " SampleClass is disposed " );
}
// 省略
}
}
在Dispose模式中,幾乎每一行都有特殊的含義。
在標準的Dispose模式中,我們注意到一個以~開頭的方法: /// <summary>
/// 必須,以備程式員忘記了顯式調用Dispose方法
/// </summary>
~ SampleClass()
{
// 必須為false
Dispose( false );
}
這個方法叫做類型的終結器。提供終結器的全部意義在於:我們不能奢望類型的調用者肯定會主動調用Dispose方法,基於終結器會被記憶體回收行程調用這個特點,終結器被用做資源釋放的補救措施。
一個類型的Dispose方法應該允許被多次調用而不拋異常。鑒於這個原因,類型內部維護了一個私人的布爾型變數disposed:
private bool disposed = false;
在實際處理代碼清理的方法中,加入了如下的判斷語句: if (disposed)
{
return ;
}
// 省略清理部分的代碼,並在方法的最後為disposed賦值為true
disposed = true ;
這意味著類型如果被清理過一次,則清理工作將不再進行。
應該注意到:在標準的Dispose模式中,真正實現IDisposable介面的Dispose方法,並沒有實際的清理工作,它實際調用的是下面這個帶布爾參數的受保護的虛方法: /// <summary>
/// 非密封類修飾用protected virtual
/// 密封類修飾用private
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose( bool disposing)
{
// 省略代碼
}
之所以提供這樣一個受保護的虛方法,是為了考慮到這個類型會被其他類繼承的情況。如果類型存在一個子類,子類也許會實現自己的Dispose模式。受保護的虛方法用來提醒子類必須在實現自己的清理方法的時候注意到父類的清理工作,即子類需要在自己的釋放方法中調用base.Dispose方法。
還有,我們應該已經注意到了真正撰寫資源釋放代碼的那個虛方法是帶有一個布爾參數的。之所以提供這個參數,是因為我們在資源釋放時要區別對待託管資源和非託管資源。
在供調用者調用的顯式釋放資源的無參Dispose方法中,調用參數是true: public void Dispose()
{
// 必須為true
Dispose( true );
// 其他省略
}
這表明,這個時候代碼要同時處理託管資源和非託管資源。
在供記憶體回收行程調用的隱式清理資源的終結器中,調用參數是false: ~ SampleClass()
{
// 必須為false