1. 程式人生 > >C#中的Dispose模式

C#中的Dispose模式

C#中的資源

在我們的程式中,使用資源後,需要釋放。那麼在C#中的每一種資源,可以分為兩類:

- 託管資源:由CLR管理分配和釋放的資源,即由CLR裡new出來的物件;
- 非託管資源:不受CLR管理的物件,windows核心物件,如檔案、資料庫連線、套接字、COM物件等;

在我們的程式中,使用到了非託管資源,或者託管資源,最後都需要釋放。針對託管資源,DotNet的垃圾回收器會自動地回收託管資源,而非託管的資源,則需要自己進行處理。

那麼,我們可以使用C#的Dispose模式來方便地釋放這些資源。

Disposal模式

要採用Dispose模式,需要讓型別繼承介面IDisposable,其標示了該型別是需要顯式釋放資源的,你需要呼叫我的Dispose方法。

先看一下IDisposable介面的定義:

namespace System
{
    //
    // 摘要:
    //     定義一種釋放分配的資源的方法。
    [ComVisible(true)]
    public interface IDisposable
    {
        //
        // 摘要:
        //     執行與釋放或重置非託管資源相關的應用程式定義的任務。
        void Dispose();
    }
}

可見,IDisposable介面很簡單。

VS對IDisposable介面實現的支援

在VS.net中,對IDispose模式實現支援的非常好。可以自動生成程式碼框架。

VS中對Dispose模式實現支援

看看自動生成的程式碼

#region IDisposable Support
private bool disposedValue = false; // 要檢測冗餘呼叫

protected virtual void Dispose(bool disposing)
{
    if (!disposedValue)
    {
        if (disposing)
        {
            // TODO: 釋放託管狀態(託管物件)。
        }

        // TODO: 釋放未託管的資源(未託管的物件)並在以下內容中替代終結器。
        // TODO: 將大型欄位設定為 null。
disposedValue = true; } } // TODO: 僅當以上 Dispose(bool disposing) 擁有用於釋放未託管資源的程式碼時才替代終結器。 // ~ResourceMgr() { // // 請勿更改此程式碼。將清理程式碼放入以上 Dispose(bool disposing) 中。 // Dispose(false); // } // 新增此程式碼以正確實現可處置模式。 public void Dispose() { // 請勿更改此程式碼。將清理程式碼放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 如果在以上內容中替代了終結器,則取消註釋以下行。 // GC.SuppressFinalize(this); } #endregion

程式碼中包含了 IDisposableDispose()方法,和其呼叫的protected virtual void Dispose(bool disposing)實現方法。
Dispose(bool)定義為protected virtual,可以在子類中進行override。

簡單解釋

實現方法藉助了一個disposedValue標示變數,在介面的Dispose方法實現中,使用引數true來呼叫Dispose(bool)進行真正的釋放操作,在操作完成後,disposedValue置為true,標示完成了Dispose;如果再次呼叫,就直接跳過。

結合 Finalize Method

一般,我們在析構器中進行清理的工作,這就是C#的Finalize方法。

~Object ();

我們可以在析構器Finalize方法中,進行必要的清理工作,以防止呼叫方沒有使用Dispose來正確地釋放資源。但要注意和Dispose的正確處理,防止多次釋放的錯誤。

最簡單地使用using語句

來自MS的示例:

using System;
using System.IO;
using System.Text.RegularExpressions;

public class WordCount
{
   private String filename = String.Empty;
   private int nWords = 0;
   private String pattern = @"\b\w+\b"; 

   public WordCount(string filename)
   {
      if (! File.Exists(filename))
         throw new FileNotFoundException("The file does not exist.");

      this.filename = filename;
      string txt = String.Empty;
      using (StreamReader sr = new StreamReader(filename)) {
         txt = sr.ReadToEnd();
      }
      nWords = Regex.Matches(txt, pattern).Count;
   }

   public string FullName
   { get { return filename; } }

   public string Name
   { get { return Path.GetFileName(filename); } }

   public int Count 
   { get { return nWords; } }
}  

使用using(resource) { ... },系統會隱式地呼叫對應資源的Dispose()方法來釋放資源。