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

.NET IDisposable 模式完整指南:從基礎用法到標準釋放模式的演進

     編輯:關於.NET

IDisposable 是 .NET 釋放非托管資源的核心協定。本文層層遞進:基礎 using/try/finally 用法 → 析構函數與 Finalize 關係 → 經典 Dispose(bool) 模式 → 完整實戰代碼範本,構建資源管理肌肉記憶。

基礎:兩種等效調用方式

// 1. using 語法糖 (編譯器展開為 try/finally)
using (var obj = new MyResource()) { obj.DoWork(); }

// 2. 顯式 try/finally (異常時仍保證釋放)
var obj = new MyResource();
try { obj.DoWork(); }
finally { (obj as IDisposable)?.Dispose(); }

注意:using 塊內拋異常時,Dispose 仍會被調用 (在 finally 中),兩者功能等價。try/catch/finally 可捕獲異常並處理,using 不能捕獲。

演進:從析構函數到 Dispose 模式

  1. 析構函數語法:~MyClass() 編譯為 protected override void Finalize() { try { ... } finally { base.Finalize(); } }。不可直接重寫 Finalize。
  2. 簡單組合:析構函數調用 Dispose()。缺陷:若用戶已手動 Dispose(),GC 仍會調用析構函數導致重複釋放。
  3. 標準 Dispose 模式 (核心):
    private bool _disposed = false;
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // 關鍵:告訴 GC 無需調用 Finalize
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 釋放托管資源 (其他 IDisposable 對象)
                _managedResource?.Dispose();
            }
            // 釋放非托管資源 (IntPtr, Handle, 文件流等)
            CloseHandle(_handle);
            _handle = IntPtr.Zero;
            _disposed = true;
        }
    }
    
    ~MyClass() { Dispose(false); } // 僅釋放非托管資源
    

關鍵點解析

  • GC.SuppressFinalize(this):移出終結隊列,避免 GC 額外開銷與重複釋放。
  • disposing=true:用戶主動調用,釋放托管+非托管資源。
  • disposing=false:GC 從 Finalize 調用,僅釋放非托管資源 (托管資源將由 GC 自動回收,不可訪問其他托管對象)。
  • _disposed 標誌防重入;Dispose(bool) 為 protected virtual 供派生類擴展。
  • 對象釋放後調用業務方法應拋 ObjectDisposedException。

完整實戰範本:基類與派生類協作

public class BaseResource : IDisposable
{
    private IntPtr _handle; // 非托管
    private Component _components; // 托管
    private bool _disposed;

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

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing) _components?.Dispose(); // 托管
            CloseHandle(_handle); _handle = IntPtr.Zero; // 非托管
            _disposed = true;
        }
    }
    ~BaseResource() { Dispose(false); }

    public void DoWork() { if (_disposed) throw new ObjectDisposedException(); }
}

public class DerivedResource : BaseResource
{
    private ManagedResource _managed;
    private NativeResource _native;
    private bool _disposed;

    protected override void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            try
            {
                if (disposing) _managed?.Dispose();
                CloseHandle(_native);
                _disposed = true;
            }
            finally { base.Dispose(disposing); } // 保證基類釋放
        }
    }
}

遵循標準模式,配合 CLR GC,能確保托管/非托管資源在任何路徑 (主動釋放/GC 回收/異常中斷) 下恰好一次被正確釋放,杜絕泄漏與重複釋放崩潰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved