.NET 垃圾收集器 (GC) 僅管理記憶體 (受控資源)。檔案控制代碼、資料庫連線、GDI 物件、COM 介面、Unmanaged 記憶體等 **非受控資源** 必須由開發者明確釋放。標準 Dispose 模式提供:
IDisposable.Dispose)。Finalize/~Class) 兜底。GC.SuppressFinalize)。protected virtual Dispose(bool))。
public class ResourceHolder : IDisposable
{
// 1. 狀態旗標
private bool _disposed = false;
// 2. 非受控資源 (範例: 檔案控制代碼)
private IntPtr _handle = IntPtr.Zero;
private SafeFileHandle _safeHandle; // 建議用 SafeHandle 封裝
// 3. 公用 Dispose - 客戶端呼叫
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 關鍵:已手動清理,告知 GC 不需呼叫終結器
}
// 4. 核心虛擬方法 - 衍生類覆寫點
protected virtual void Dispose(bool disposing)
{
if (_disposed) return; // 防禦性:多次呼叫安全
if (disposing)
{
// 釋放受控資源 (Managed): 其他 IDisposable 物件、事件取消訂閲等
_safeHandle?.Dispose();
_safeHandle = null;
}
// 釋放非受控資源 (Unmanaged): 總是執行
if (_handle != IntPtr.Zero)
{
CloseHandle(_handle); // P/Invoke
_handle = IntPtr.Zero;
}
_disposed = true;
}
// 5. 終結器 - 最終防線
~ResourceHolder()
{
Dispose(false); // disposing=false:僅清理非受控資源
}
// 6. 公開方法檢查 disposed 狀態
public void DoSomething()
{
if (_disposed) throw new ObjectDisposedException(nameof(ResourceHolder));
// 正常邏輯...
}
// P/Invoke 範例
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
}
public class DerivedResource : ResourceHolder
{
private bool _disposed = false;
private MemoryStream _managedStream;
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 先釋放自己的受控資源
_managedStream?.Dispose();
_managedStream = null;
}
// 再釋放自己的非受控資源 (若有)
// ...
// 最後呼叫基類 Dispose (基類會設定 _disposed=true)
base.Dispose(disposing);
_disposed = true; // 衍生類自己的旗標
}
}
| 情境 | Dispose(true) | Dispose(false) / 終結器 |
|---|---|---|
| 受控資源 (Managed) | ✅ 釋放 | ❌ 不觸碰 (可能已被 GC 回收) |
| 非受控資源 (Unmanaged) | ✅ 釋放 | ✅ 釋放 (最後機會) |
| 呼叫基類 | ✅ base.Dispose(true) | ✅ base.Dispose(false) |
| GC.SuppressFinalize | ✅ 在公用 Dispose() | ❌ 從不呼叫 |
public class ModernResource : IDisposable
{
private SafeFileHandle _handle; // 衍生自 SafeHandleZeroOrMinusOneIsInvalid
private bool _disposed;
public ModernResource(string path)
{
_handle = File.OpenHandle(path); // .NET 6+ 回傳 SafeFileHandle
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) _handle?.Dispose(); // SafeHandle 內建終結器,雙重保障
_disposed = true;
}
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
~ModernResource() { Dispose(false); }
}
SafeHandle 內建終結器、參考計數、Critical Finalizer Object (CFO) 特性,極大降低資源洩漏風險。
GlobalList.Add(this)) → ✅ 絕不復活,導致終結器不再執行、資源永不釋放。Dispose 拋出例外 → ✅ 釋放程式碼不應拋出例外,記錄錯誤即可。GC.SuppressFinalize → ✅ 公用 Dispose() 必須呼叫,避免效能損耗。IDisposable 但無終結器 (含非受控資源時) → ✅ 有非受控資源必須有終結器。
// 傳統
using (var res = new ResourceHolder()) { res.DoSomething(); }
// C# 8+ using 宣告 (編譯器自動在作用域結束呼叫 Dispose)
using var res = new ResourceHolder();
res.DoSomething();
// 隱含 try/finally 區塊結束時呼叫 Dispose()