在 .NET 管理非受控資源(檔案控制代碼、資料庫連線、COM 物件等)時,正確釋放資源至關重要。Finalize (析構函數)、Dispose、Close 三者雖同樣涉及資源釋放,但觸發時機、語意與最佳實踐截然不同。
| 特性 | 析構函數 (Finalize) | Dispose() | Close() |
|---|---|---|---|
| 語意 | 銷毀物件,釋放非受控資源 | 釋放所有資源 (受控+非受控) | 關閉/釋放資源,視類別設計而定 |
| 調用方式 | GC 自動調用,不可手動呼叫 | 開發人員顯式調用或 using 區塊 | 開發人員顯式調用 |
| 調用時機 | 非確定性 (GC 決定) | 確定性 (離開 using 或手動呼叫) | 確定性 |
| 可重複使用 | 否 | 否 (通常) | 視類別而定 (如 SqlConnection 可 Reopen) |
| 介面規範 | 無 | IDisposable | 無標準介面 |
~ClassName() { ... },編譯器轉為 override void Finalize()。IDisposable,提供 public void Dispose()。protected virtual void Dispose(bool disposing) 區分來源:public class ResourceHolder : IDisposable
{
private IntPtr _unmanagedHandle;
private AnotherDisposable _managedResource;
// 公開 Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 告訴 GC 不需再呼叫 Finalize
}
// 核心邏輯
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// 釋放受控資源
_managedResource?.Dispose();
}
// 釋放非受控資源 (兩種情況都要)
ReleaseUnmanaged(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero;
}
// 析構函數:僅釋放非受控
~ResourceHolder() => Dispose(false);
}
關鍵點: GC.SuppressFinalize(this) 避免重複釋放並提升 GC 效能。
SqlConnection.Close():歸還連線至連線池,物件可重用 Open()。FileStream.Close():相當於 Dispose(),釋放檔案控制代碼。Close 與 Dispose 且皆為 public,通常 Close 呼叫 Dispose,或 Close 只做「邏輯關閉」,Dispose 做「實際釋放」。using:確保 Dispose 確定被呼叫。
using (var conn = new SqlConnection(cs)) { ... } // 編譯器產生 try/finally + Dispose
Close() (如 SqlConnection),保留物件以便重用。Dispose() 或離開 using 區塊。IDisposable,無需 Finalize。Finalize 是安全網,Dispose 是主力,Close 是語意便利。 理解三者分工,才能寫出健壯、高效能的 .NET 程式。