接口IDisposable的用法

  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語句代碼塊內,可使用聲明的對象,當語句離開代碼塊後,系統自動釋放資源:ide

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

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

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

  在實現IDisposable接口時,其Dispose()方法並無作實際的清理工做,但提供了帶bool參數的受保護的虛方法。由於該類型可能被其餘類繼承,若是子類實現本身的Dispose模式,受保護的虛方法能夠提醒子類:在實現本身的清理方法時,須要注意父類的清理工做(base.Dispose方法)。線程

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

  Dispose模式設計思路:若是調用者顯示調用了Dispose方法,那麼類型循序漸進釋放本身的所有資源,而後通知垃圾回收器不須要再釋放(GC.SuppressFinalize()方法);而忘記調用Dispose方法,那麼類型就假定本身的全部託管資源會所有交給垃圾回收器回收,不須要手動清理。指針

相關文章
相關標籤/搜索