建議46:顯式釋放資源需繼承接口IDisposable
C#中的每一個類型都代表一種資源,資源分為兩類:
托管資源:由CLR管理分配和釋放的資源,即從CLR裡new出來的對象。
非托管資源:不受CLR管理的對象,如Windows內核對象,或者文件、數據庫連接、套接字、COOM對象等。
如果我們的類型使用了非托管資源,或者需要顯示地釋放托管資源,那麼就需要讓類型繼承接口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"); } //省略 } } class AnotherResource : IDisposable { public void Dispose() { } }
繼承IDisposable接口也為實現語法糖using帶來了便利。如:
using (SampleClass cl = new SampleClass()) { //省略 }
等價於:
SampleClass cl; try { cl == new SampleClass(); //省略 } finally { cl.Dispose(); }
如果存在兩個類型一致的對象,using還可以這樣使用:
using (SampleClass c1 = new SampleClass(),c2=new SampleClass()) { //省略 }
如果兩個類型不一致,則可以這樣使用:
using (SampleClass c1 = new SampleClass()) using (SampleAnotherClass c2 = new SampleAnotherClass()) { //省略 }
轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技