首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# Dispose 模式完整解析:Finalize、Dispose(bool) 與 IDisposable 最佳實務

     編輯:關於C語言

核心問題

.NET 垃圾收集器 (GC) 僅管理受控記憶體。非受控資源 (檔案控制代碼、資料庫連線、GDI+ 物件、COM 物件、記憶體指標等) 必須由開發者明確釋放。IDisposable 模式提供決定性釋放機制。

三個關鍵成員比較

成員呼叫時機呼叫者用途
~Finalize() (析構函式)GC 回收物件時 (非決定性)Finalizer Thread防禦性釋放非受控資源,防止使用者忘記呼叫 Dispose
Dispose()使用者明確呼叫 (決定性)程式碼撰寫者立即釋放受控 + 非受控資源,並呼叫 GC.SuppressFinalize(this)
Dispose(bool disposing)由上述兩者內部呼叫類別本身核心釋放邏輯:disposing=true 釋放受控+非受控;false 僅釋放非受控

標準實作範本 (基底類別)

public class BaseResource : IDisposable
{
    private bool _disposed = false;
    private OtherManagedResource _managed = new OtherManagedResource();
    private IntPtr _unmanagedHandle = IntPtr.Zero; // 非受控資源代碼

    // 1. 公開 Dispose - 供使用者呼叫
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // 通知 GC 不需再呼叫 Finalize
    }

    // 2. 核心虛擬方法 - 供衍生類別覆寫
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 釋放受控資源
            _managed?.Dispose();
            _managed = null;
        }

        // 釋放非受控資源 (無論 disposing 真偽)
        if (_unmanagedHandle != IntPtr.Zero)
        {
            CloseHandle(_unmanagedHandle); // P/Invoke 呼叫原生 API
            _unmanagedHandle = IntPtr.Zero;
        }

        _disposed = true;
    }

    // 3. 析構函式 - 防禦性最終防線
    ~BaseResource()
    {
        Dispose(false); // 僅清理非受控資源
    }

    // 範例:P/Invoke 宣告
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hObject);
}

衍生類別正確覆寫模式

public class DerivedResource : BaseResource
{
    private bool _disposed = false;
    private AnotherManaged _child = new AnotherManaged();

    protected override void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            _child?.Dispose(); // 釋放自身受控資源
            _child = null;
        }

        // 釋放自身非受控資源 (若有)
        // ...

        // 最後呼叫基底類別 Dispose
        base.Dispose(disposing);

        _disposed = true;
    }
}

關鍵規則總結

  1. 非受控資源:在 Dispose(bool) 中無論 disposing 參數皆須釋放。
  2. 受控資源:僅在 disposing == true 時釋放 (Finalize 階段其他受控物件可能已被回收)。
  3. 防禦性程式碼:_disposed 防止重複釋放;公開方法檢查並拋 ObjectDisposedException。
  4. GC.SuppressFinalize(this):Dispose() 中必須呼叫,避免 Finalizer 執行緒額外負擔。
  5. 基底類別先實作完整模式,衍生類別才覆寫 Dispose(bool) 並呼叫 base.Dispose(disposing)。
  6. 密封類別 (sealed) 可簡化:無需 virtual、protected,直接實作 Dispose() 即可。
  7. using 陳述式:using (var r = new Resource()) { ... } 編譯為 try/finally 自動呼叫 Dispose()。

常見錯誤

  • 在 Finalize 中存取其他受控物件 (可能已被終結)。
  • 忘記呼叫 base.Dispose(disposing) 導致基底資源洩漏。
  • Dispose() 未呼叫 GC.SuppressFinalize。
  • 實作 IDisposable 卻未實作 Finalize,且類別持有非受控資源。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved