.NET GC 僅管理託管記憶體。以下情況需開發者介入:
using System;
class ResourceHolder : IDisposable {
private IntPtr _nativeHandle = IntPtr.Zero; // 非託管資源
private bool _disposed = false;
// 1. 公共 Dispose:供呼叫端顯式釋放
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this); // 通知 GC 不需再呼叫 Finalize
}
// 2. 核心釋放邏輯:disposing=true 來自 Dispose(),false 來自 Finalizer
protected virtual void Dispose(bool disposing) {
if (_disposed) return;
if (disposing) {
// 釋放託管資源(其他 IDisposable 物件、事件訂閱等)
// managedResource?.Dispose();
}
// 釋放非託管資源(無論 disposing 為何)
if (_nativeHandle != IntPtr.Zero) {
NativeMethods.CloseHandle(_nativeHandle);
_nativeHandle = IntPtr.Zero;
}
_disposed = true;
}
// 3. 終結器:GC 回收時的安全網
~ResourceHolder() {
Dispose(false);
}
// 4. 公開方法使用資源時檢查
public void DoWork() {
if (_disposed) throw new ObjectDisposedException(nameof(ResourceHolder));
// 使用 _nativeHandle
}
}
// P/Invoke 宣告
internal static class NativeMethods {
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hObject);
}
| 元素 | 用途 | 關鍵點 |
|---|---|---|
Dispose() | 公共介面,呼叫端主動釋放 | 呼叫 Dispose(true) + GC.SuppressFinalize |
Dispose(bool) | 核心邏輯,virtual 供衍生類別擴充 | disposing=true:釋放託管+非託管;false:僅非託管 |
~Finalizer() | 安全網,防止呼叫端忘記 Dispose | 呼叫 Dispose(false),不可存取託管物件(可能已被回收) |
GC.SuppressFinalize | 優化:移出 Finalization Queue | 已手動釋放的物件無需 GC 執行 Finalizer,減少 GC 壓力 |
_disposed 旗標 | 防止重複釋放 | 多執行緒環境建議用 Interlocked.Exchange |
class DerivedHolder : ResourceHolder {
private readonly Stream _stream; // 託管資源
public DerivedHolder() => _stream = new MemoryStream();
protected override void Dispose(bool disposing) {
if (disposing) {
_stream?.Dispose(); // 釋放衍生類別的託管資源
}
// 必須呼叫基底 Dispose 釋放非託管資源
base.Dispose(disposing);
}
}
using (var holder = new ResourceHolder()) {
holder.DoWork();
} // 編譯器產生 try/finally 確保 Dispose() 被呼叫
NullReferenceException 或存取已釋放資源GC.SuppressFinalize 不執行,Finalizer 仍會跑SafeHandle 衍生類別:.NET 2.0+ 建議用 SafeHandle 替代手動 IntPtr + Finalizer,自動處理參考計數、關鍵區域
class MySafeHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid {
public MySafeHandle() : base(true) { }
protected override bool ReleaseHandle() => NativeMethods.CloseHandle(handle);
}
// 使用
using (var h = new MySafeHandle()) {
NativeMethods.DoSomething(h);
} // 自動呼叫 ReleaseHandle,無需 Finalizer
SafeHandle 內建關鍵區域保護、參考計數、可靠性契約,是處理非託管資源的現代標準。