在 C# 物件導向程式設計中,建構函式與解構函式分別掌控物件的誕生與終結。雖然語法簡單,但涉及記憶體管理、資源釋放、繼承鏈初始化順序等核心機制。本文系統整理建構函式的四種類型、繼承情境下的執行順序、解構函式與垃圾收集器(GC)的互動關係,以及非受控資源釋放的標準模式,幫助開發者避開常見陷阱。
void 都不能寫)class TestClass
{
// 編譯器自動產生,等同於:
public TestClass() : base() { }
}
僅在類別未定義任何建構函式時存在。一旦自訂任何建構函式,預設建構函式即消失。
class Point
{
public double X, Y;
public Point() // 無參數
{ X = 0; Y = 0; }
public Point(double x, double y) // 兩參數
{ X = x; Y = y; }
}
支援多載,提供多種初始化路徑。注意:定義任一建構函式後,若仍需無參數版本,必須顯式撰寫。
class Employee
{
private static DataSet _ds;
static Employee() // 無修飾詞、無參數、不可呼叫
{
_ds = new DataSet(); // 初始化靜態資料
}
}
class Singleton
{
private Singleton() { } // 防止外部實例化
public static Singleton Instance { get; } = new Singleton();
}
配合靜態工廠方法實現單例、工廠模式或純靜態工具類別。
class A { public A() { Console.WriteLine("A()"); } public A(int i) { Console.WriteLine($"A({i})"); } }
class B : A
{
public B() { Console.WriteLine("B()"); }
public B(int i) : base(i) { Console.WriteLine($"B({i})"); } // 顯式呼叫基類有參建構函式
public B(int i, int j) : base(i) { Console.WriteLine($"B({i},{j})"); }
}
base() 或 base(參數))base(參數) 顯式呼叫class ResourceHolder
{
~ResourceHolder() // 無修飾詞、無參數、不可呼叫、無法多載
{
// 清理非受控資源
}
}
Finalize()(即 ~ClassName())效能代價:有解構函式的物件至少經歷兩次 GC 週期才被回收,且 Finalizer 執行緒阻塞會影響整個應用程式。
| 資源類型 | 範例 | 釋放方式 |
|---|---|---|
| 受控資源 | 記憶體、陣列、List、DataSet、其他 Managed 物件 | GC 自動回收,無需介入 |
| 非受控資源 | 檔案控制代碼、資料庫連線、GDI+ 物件、COM 物件、記憶體指標 | 必須手動釋放,通常在解構函式或 Dispose() 中 |
解構函式是最後防線,Dispose 模式才是資源釋放的主流程:
public class ResourceWrapper : IDisposable
{
private IntPtr _unmanagedHandle = IntPtr.Zero;
private bool _disposed = false;
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true); // 受控包裝器
// 公開 Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 告訴 GC 不需呼叫 Finalize
}
// 核心邏輯:disposing=true 來自 Dispose(),false 來自 Finalizer
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 釋放受控資源
_safeHandle?.Dispose();
}
// 釋放非受控資源(無論 disposing 真偽)
if (_unmanagedHandle != IntPtr.Zero)
{
CloseHandle(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
// 解構函式作為安全網
~ResourceWrapper()
{
Dispose(false);
}
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
}
// using 區塊確保 Dispose 被呼叫
using (var resource = new ResourceWrapper())
{
// 使用資源
} // 隱式呼叫 Dispose()
// 或手動控制
var resource = new ResourceWrapper();
try { /* 使用 */ }
finally { resource.Dispose(); }
GC.SuppressFinalize:造成雙重清理與效能損耗sealed 類別無需 protected virtual Dispose(bool)Dispose() 或 GC 觸發SafeHandle 衍生類別(SafeFileHandle, SafeRegistryHandle 等)自動實作 IDisposable 並處理 Finalize 邏輯using 宣告簡化範圍管理:using var fs = File.OpenRead("x");IntPtr 操作非受控記憶體,改用 Marshal 類別或 Span/Memory建構函式確保物件「生而正確」,解構函式與 IDisposable 確保物件「死而無憾」。掌握繼承鏈初始化順序、GC 代際機制、Dispose 標準模式,是寫出健壯、高效能 C# 程式的關鍵。記住核心原則:受控資源交給 GC,非受控資源用 Dispose 模式主動釋放,解構函式僅作為最後安全網。