1. 程式人生 > >接口IDisposable的用法

接口IDisposable的用法

oid 補救措施 marshal ron 回收對象 dex 不必要 vat 們的

  C#的每一個類型都代表一種資源,而資源又分為兩類:

  • 托管資源 由CLR管理分配和釋放的資源,即從CLR裏new出來的對象。
  • 非托管資源 不受CLR管理的對象,如Windows內核對象,或者文件、數據庫連接、套接字、COM對象等。

  如果類型用到了非托管資源,或者需要顯式釋放托管資源,那麽需要讓類型繼承接口IDisposable。記住:如果類型需要顯式釋放資源,那麽一定要繼承IDisposable接口。如:

技術分享圖片
class SampleClass:IDisposable
{
    private IntPtr nativeResource = Marshal.AllocHGlobal(100
);//非托管資源 private Bitmap bitmap = new Bitmap(100, 100);//托管資源 private bool isDisposed = false; //實現IDisposable中的Dispose方法 public void Dispose( ) { Dispose(true); GC.SuppressFinalize(this);//通知垃圾回收器不用再調用終結器 } //不必要的方法,只是為了符合其他語言的規範 public void Close() { Dispose(); }
//必須的,防止程序員忘記顯示調用Dispose方法(隱式清理) ~SampleClass() { Dispose(false); } //非密封類修飾用protected virtual,提醒子類必須實現自己的清理方法時註意到父類的清理工作 protected virtual void Dispose(bool isDisposing) { if(isDisposed) { return; } if(isDisposing) {
//清理托管資源 if(bitmap != null) { bitmap.Dispose(); bitmap = null; } } //清理非托管資源 if(nativeResource!=IntPtr.Zero) { Marshal.FreeHGlobal(nativeResource); nativeResource = IntPtr.Zero; } isDisposed = false; } public void SamplePublicMethod() { if(isDisposed) { throw new ObjectDisposedException("SampleClass", "SampleClass is disposed"); } //代碼 } }
View Code

  繼承IDisposable接口,可以使用using語法糖。在using語句代碼塊內,可以使用聲明的對象,當語句離開代碼塊後,系統自動釋放資源:

技術分享圖片
//使用using方法,當語句離開代碼塊後,using內的對象自動釋放
using (SampleClass sample = new SampleClass())
{
    //……
}
//以上代碼相當於下面的代碼
SampleClass sample0 = new SampleClass();
try
{
    //……
}
finally
{
    sample0.Dispose();
}
View Code

  在SampleClass中,存在一個終結器(C++中叫析構器)。其意義在於,調用者可能並不會主動調用Dispose方法,而終結器會被垃圾回收器調用調用,所以它作為釋放資源的補救措施。

  在CLR中,每new一個對象時,就會為該對象在堆上分配內存,如果不再被引用,就會回收它們的內存。如果沒有實現IDisposable接口的類型對象,垃圾回收器會直接釋放對象所占內存;如果實現了,每次創建對象時,CLR會將該對象的一個指針放到終結列表中,垃圾回收器在回收對象前會首先將終結列表中的指針放入一個freachable隊列。同時,CLR會分配一個線程管理freachable隊列,調用對象終結器,只有此時,對象才會被真正標識為垃圾,並在下一次進行垃圾回收時釋放對象所占內存。即:實現IDisposable接口的類型,至少要經過兩次垃圾回收才能真正釋放掉內存。其中Dispose方法中的GC.SuppressFinalize()方法用於在顯示釋放資源後,通知垃圾回收器不用再調用終結器(隱式回收)釋放資源。

  在實現IDisposable接口時,其Dispose()方法並沒有做實際的清理工作,但提供了帶bool參數的受保護的虛方法。因為該類型可能被其他類繼承,如果子類實現自己的Dispose模式,受保護的虛方法可以提醒子類:在實現自己的清理方法時,需要註意父類的清理工作(base.Dispose方法)。

  真正撰寫資源釋放代碼的虛方法有一個bool參數,但是在顯示釋放資源(true)與隱式釋放資源(false)調用中傳入的參數不同。表明:隱式清理時,只需要處理非托管資源就行。托管資源中的普通類型不需要手動清理,而非普通類型需要手動清理。

  Dispose模式設計思路:如果調用者顯示調用了Dispose方法,那麽類型按部就班釋放自己的全部資源,然後通知垃圾回收器不需要再釋放(GC.SuppressFinalize()方法);而忘記調用Dispose方法,那麽類型就假定自己的所有托管資源會全部交給垃圾回收器回收,不需要手動清理。

接口IDisposable的用法