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 不能捕獲。
~MyClass() 編譯為 protected override void Finalize() { try { ... } finally { base.Finalize(); } }。不可直接重寫 Finalize。Dispose()。缺陷:若用戶已手動 Dispose(),GC 仍會調用析構函數導致重複釋放。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 回收/異常中斷) 下恰好一次被正確釋放,杜絕泄漏與重複釋放崩潰。