.NET中IDisposable接口的基本使用

 

首先來看MSDN中關於這個接口的說明:

[ComVisible(true)]
public interface IDisposable
{
    // Methods
    void Dispose();
}
1.[ComVisible(true)]:指示該託管類型對 COM 是可見的.

2.此接口的主要用途是釋放非託管資源。當不再使用託管對象時,垃圾回收器會自動釋放分配給該對象的內存。但無法預測進行垃圾回收的時間。另外,垃圾回收器對窗口句柄或打開的文件和流等非託管資源一無所知。將此接口的Dispose方法與垃圾回收器一起使用來顯式釋放非託管資源。當不再需要對象時,對象的使用者可以調用此方法。

一:基本應用

1.我們來定義一個實現了IDisposable接口的類,代碼如下:

 public class CaryClass :IDisposable
{ public void DoSomething() { Console.WriteLine("Do some thing...."); } public void Dispose() { Console.WriteLine("及時釋放資源"); } }
2.我們有兩種方式來調用:
2.1.第一種方式,使用Using語句會自動調用Dispose方法,代碼如下:
  using (CaryClass caryClass = new CaryClass())
  {
      caryClass.DoSomething();
  }
2.2第二種方式,現實調用該接口的Dispose方法,代碼如下:
  CaryClass caryClass = new CaryClass();
   try
    {
       caryClass.DoSomething();               
    }
   finally
    {
       IDisposable disposable = caryClass as IDisposable;
       if (disposable != null) disposable.Dispose();
    }
兩種方式的執行結果是一樣的,如下圖:
 
2.3.使用try/finally 塊比使用 using 塊的好處是即使using中的代碼引發異常,CaryClass的Dispose方法仍有機
會清理該對象。所以從這裏看還是使用try/catch好一些。

二:Disposable 模式
1.在.NET種由於當對象變爲不可訪問後將自動調用Finalize方法,所以我們手動調用IDisposable接口的Dispose方法
和對象終結器調用的方法極其類似,我們最好將他們放到一起來處理。我們首先想到的是重寫Finalize方法,如下:
protected override void Finalize()
{
     Console.WritleLine("析構函數執行...");
}
當我們編譯這段代碼的時候,我們發現編譯器會報如下的錯誤:
 
這是因爲編譯器徹底屏蔽了父類的Finalize方法,編譯器提示我們如果要重寫Finalize方法我們要提供一個析構函數來
代替,下面我們就提供一個析構函數:
  ~CaryClass()
  {
      Console.WriteLine("析構函數執行...");
  }
實際上這個析構函數編譯器會將其轉變爲如下代碼:
protected override void Finalize()
{
   try
   {
     Console.WritleLine("析構函數執行...");
   }
   finally
   {
     base.Finalize();
   }
}
2.然後我們就可以將Dispose方法的調用和對象的終結器放在一起來處理,如下:
public class CaryClass: IDisposable
{
    ~CaryClass()
    {
        Dispose();
    }
    public void Dispose()
    {
        // 清理資源
}
}
3.上面實現方式實際上調用了Dispose方法和Finalize方法,這樣就有可能導致做重複的清理工作,所以就有了下面經典
Disposable 模式:
 private bool IsDisposed=false;  
 public void Dispose()  
 {  
     Dispose(true);  
     GC.SupressFinalize(this);  
 }  
 protected void Dispose(bool Diposing)  
 {  
     if(!IsDisposed)  
     {  
         if(Disposing)  
         {  
            //清理託管資源
} //清理非託管資源 } IsDisposed=true; } ~CaryClass() { Dispose(false); }

3.1. SupressFinalize方法以防止垃圾回收器對不需要終止的對象調用 Object.Finalize()。
3.2. 使用IDisposable.Dispose 方法,用戶可以在可將對象作爲垃圾回收之前隨時釋放資源。如果調用了 IDisposable.Dispose 方法,此方法會釋放對象的資源。這樣,就沒有必要進行終止。IDisposable.Dispose 應調用 GC.SuppressFinalize 以使垃圾回收器不調用對象的終結器。
3.3.我們不希望Dispose(bool Diposing)方法被外部調用,所以他的訪問級別爲protected 。如果Diposing爲true則釋放託管資源和非託管資源,如果 Diposing等於false則該方法已由運行庫從終結器內部調用,並且只能釋放非託管資源。
3.4.如果在對象被釋放後調用其他方法,則可能會引發 ObjectDisposedException。

三:實例解析

public class BaseResource : IDisposable
    {
        // 非託管資源
        private IntPtr handle;
        //託管資源
        private Component Components;
        // Dispose是否被調用
        private bool disposed = false;

        public BaseResource()
        {            
        }
       
        public void Dispose()
        {
            Dispose(true);            
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
           
            if (!this.disposed)          
            {               
                if (disposing)
                {
                    // 釋放託管資源
                    Components.Dispose();
                }
                // 釋放非託管資源,如果disposing爲false, 
                // 只有託管資源被釋放
                CloseHandle(handle);
                handle = IntPtr.Zero;
                // 注意這裏不是線程安全的
            }
            disposed = true;
        }

        // 析構函數只會在我們沒有直接調用Dispose方法的時候調用
        // 派生類中不用在次提供析構函數
        ~BaseResource()
        {
            Dispose(false);
        }

        // 如果你已經調用了Dispose方法後在調用其他方法會拋出ObjectDisposedException
        public void DoSomething()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException();
            }
        }
    }

    
    public class MyResourceWrapper : BaseResource
    {
        // 託管資源
        private ManagedResource addedManaged;
        // 非託管資源
        private NativeResource addedNative;
        private bool disposed = false;
       
        public MyResourceWrapper()
        {           
        }

        protected override void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                try
                {
                    if (disposing)
                    {                        
                        addedManaged.Dispose();
                    }
                    
                    CloseHandle(addedNative);
                    this.disposed = true;
                }
                finally
                {                   
                    base.Dispose(disposing);
                }
            }
        }
    }

2.使用CLR垃圾收集器,您不必再擔心如何管理對託管堆分配的內存,不過您仍需清理其他類型的資源。託管類通過
IDisposable 接口使其使用方可以在垃圾收集器終結對象前釋放可能很重要的資源。通過遵循 disposable 模式並且留
意需注意的問題,類可以確保其所有資源得以正確清理,並且在直接通過 Dispose 調用或通過終結器線程運行清理代碼時
不會發生任何問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章