**靜態建構函數** 是屬於**類別本身**而非實例的特殊方法,用於**初始化靜態欄位/屬性**或執行**類別級別的一次性設定**(如註冊依賴、載入原生庫、設定靜態快取)。
public class MyClass
{
// 靜態建構函數:無存取修飾詞、無參數、名稱與類別同名、前綴 static
static MyClass()
{
// 初始化靜態成員
StaticField = ComputeInitialValue();
NativeLibrary.Load("mylib.dll");
}
public static int StaticField { get; private set; }
}
| # | 規則 | 說明與範例 |
|---|---|---|
| 1 | 無存取修飾詞、無參數 | 由 CLR 自動呼叫,不需 `public`/`private`;無法手動呼叫,故無參數。 |
| 2 | 自動觸發時機 | 在**建立第一個實例** 或 **存取任意靜態成員** 之前,由 CLR 保證執行一次。時機非精確可控(受 JIT、型別初始化優化影響)。 |
| 3 | 每個類別僅能有一個 | 重複定義會編譯錯誤。 |
| 4 | 可與無參數實例建構函數共存 | 雖然簽章相同(無參數),但一個屬於型別、一個屬於實例,CLR 依上下文區分,不衝突。 |
| 5 | 生命週期中至多執行一次 | 即使多線程同時觸發,CLR 保證線程安全、僅執行一次(內部有鎖機制)。 |
| 6 | 不可被繼承 | 子類別有自己的靜態建構函數;父類別的靜態建構函數在子類別首次使用前執行(若父類別有靜態成員被子類別引用)。 |
| 7 | 編譯器自動產生預設靜態建構函數 | 若類別包含**帶初始值的靜態欄位**(如 `static int X = 5;`),編譯器會隱式生成靜態建構函數執行初始化。若同時寫了 `static MyClass()`,則合併執行。 |
C# 編譯器預設為類別標記 `BeforeFieldInit` 屬性,允許 CLR **在任意靜態成員首次被存取前的任意時間** 執行靜態建構函數(可提早、可延遲,利於效能)。若類別**顯式定義了靜態建構函數**,則**移除 `BeforeFieldInit`**,改為**精確語義**:必須在首次存取靜態成員或建立實例**之前**執行,時機更可預測。
// 有靜態建構函數 → 精確語義
class WithStaticCtor
{
static WithStaticCtor() { Console.WriteLine("靜態建構函數執行"); }
public static int Value = 10;
}
// 無靜態建構函數,只有靜態欄位初始化 → BeforeFieldInit
class WithoutStaticCtor
{
public static int Value = Compute(); // 可能在 Main 開始前、或首次存取 Value 時才跑
static int Compute() { Console.WriteLine("欄位初始化"); return 20; }
}
public sealed class Singleton
{
// 唯讀靜態欄位 + 靜態建構函數 = 完美線程安全延遲初始化
private static readonly Singleton _instance;
public static Singleton Instance => _instance;
static Singleton()
{
_instance = new Singleton(); // 只有在第一次存取 Instance 時才執行
}
private Singleton() { /* 私有建構函數防外部實例化 */ }
public void DoWork() => Console.WriteLine("Singleton working...");
}
靜態建構函數是 **C# 型別初始化的核心機制**,提供**線程安全、一次性、延遲執行**的靜態狀態初始化保證。掌握其**觸發時機(BeforeFieldInit)、例外行為、不可繼承、單例應用**等特性,能寫出更健壯、高效的類別庫代碼。記住:**靜態建構函數只做「純初始化」,不做「商業邏輯/IO/阻塞」**。