許多開發者誤以為實作解構函式(~MyClass())即可釋放非託管資源。實則不然:
Finalize。Finalize 後,物件會升代到 Gen 1,甚至 Gen 2,**在記憶體中存活時間大幅延長**。結論:僅使用 Finalize 釋放非託管資源會造成資源佔用延遲、效能下降,甚至資源洩漏。
正確做法是實作 IDisposable 介面,提供確定性釋放機制:
public class MyResource : IDisposable
{
private bool _disposed = false; // 防止重複釋放
private IntPtr _unmanagedHandle; // 非託管資源句柄
private Component _managedResource; // 託管資源
// 公開 Dispose 方法
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 告訴 GC 不需再呼叫 Finalize
}
// 核心虛擬方法,支援繼承鏈擴展
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 釋放託管資源(其他 IDisposable 物件)
_managedResource?.Dispose();
_managedResource = null;
}
// 釋放非託管資源(一定要釋放)
if (_unmanagedHandle != IntPtr.Zero)
{
CloseHandle(_unmanagedHandle); // P/Invoke 釋放
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
// 保底 Finalize,防止使用者忘記呼叫 Dispose
~MyResource()
{
Dispose(false);
}
// P/Invoke 宣告
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
}
public class DerivedResource : MyResource
{
private bool _disposed = false;
private SafeHandle _anotherHandle; // 建議使用 SafeHandle 封裝非託管資源
protected override void Dispose(bool disposing)
{
if (_disposed) return;
try
{
if (disposing)
{
// 釋放衍生類自己的託管資源
_anotherHandle?.Dispose();
_anotherHandle = null;
}
// 釋放衍生類自己的非託管資源
// ...
}
finally
{
// **務必呼叫基類 Dispose**,確保釋放鏈完整傳播
base.Dispose(disposing);
_disposed = true;
}
}
}
SafeHandle 替代原始 IntPtr.NET 2.0+ 提供 SafeHandle 衍生類(如 SafeFileHandle、SafeRegistryHandle),自動處理參考計數、執行緒安全、Finalize 保底,極大降低錯誤風險。
GC.SuppressFinalize(this) 的必要性若 Dispose(true) 已完整釋放所有資源,呼叫此方法可將物件從終結佇列移除,避免 GC 額外開銷,提升效能。
disposing 參數含義| disposing 值 | 呼叫來源 | 應釋放資源 |
|---|---|---|
| true | 使用者主動呼叫 Dispose() | 託管 + 非託管 |
| false | GC 執行 Finalize | 僅非託管(託管資源將由 GC 自動回收) |
IDisposable 的類別應密封或提供 protected virtual Dispose(bool)確保繼承者能正確擴展釋放邏輯而不破壞基類行為。
using 陳述式// 編譯器自動展開為 try/finally,保證 Dispose 被呼叫
using (var resource = new MyResource())
{
resource.DoWork();
}
// 此處自動呼叫 resource.Dispose()
public void Dispose() + protected virtual void Dispose(bool) + 保底 Finalize。掌握正確的資源釋放模式,是編寫健壯、高效能 C# 應用的基石。