首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 靜態建構函式與靜態唯讀欄位深度解析:執行時機、執行順序與初始化陷阱

     編輯:關於C#

static constructor (靜態建構函式) 是 C# 類別初始化的關鍵機制,與 static readonly 欄位搭配可實現**執行緒安全、一次性、延遲**的靜態資料初始化。本文深度解析執行時機、循環相依順序、IL 差異與常見陷阱。

語法規則

  • 宣告:static ClassName() { ... } (無存取修飾詞、無參數、不可繼承、不可直接呼叫)
  • 每個類別**最多一個**靜態建構函式
  • static readonly 欄位只能在宣告處或靜態建構函式指派

執行時機保證 (CLR 規格)

  1. 延遲初始化:在**第一次**存取該類別的任何 **static 成員** (欄位、屬性、方法) 或 **建立實例** 之前執行。
  2. 執行緒安全:CLR 保證多執行緒環境下**僅執行一次**,內部有鎖機制。
  3. 優先於實例建構函式:靜態建構函式在第一個實例建構函式前完成。
  4. 循環相依與執行順序經典案例

    
    class A
    {
        public static int X;
        static A()
        {
            Console.WriteLine("static A()");
            X = B.Y + 1; // 觸發 B 初始化
        }
    }
    
    class B
    {
        public static int Y = A.X + 1; // 欄位初始化器 (靜態建構函式前執行)
        static B()
        {
            Console.WriteLine("static B()");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine($"X={A.X}, Y={B.Y}"); // 輸出順序見下
        }
    }
    

    執行順序輸出

    
    static B()    // 1. Main 觸發 A 初始化 -> A 靜態建構函式執行前先跑欄位初始化器? 
    // 實際順序:
    // 1. Main 存取 A.X -> 觸發 A 初始化
    // 2. A 靜態建構函式開始執行
    // 3. A 靜態建構函式執行 X = B.Y + 1 -> 觸發 B 初始化
    // 4. B 欄位初始化器 Y = A.X + 1 執行 (此時 A.X 尚為 0) -> Y = 1
    // 5. B 靜態建構函式執行 (無碼)
    // 6. 返回 A 靜態建構函式 -> X = B.Y + 1 = 2
    // 7. 返回 Main -> 輸出 X=2, Y=1
    

    關鍵點:欄位初始化器在靜態建構函式**之前**執行,且**按文字順序**。循環相依會導致「看到對方尚未完全初始化的狀態」(上例中 B.Y 讀取 A.X 時為 0)。

    IL 層面差異:BeforeFieldInit

    類別標記觸發時機效能
    有靜態建構函式精確語義:首次存取 static 成員前稍慢 (需同步檢查)
    無靜態建構函式 (僅欄位初始化器)BeforeFieldInit:任意時機 (可能極早,如載入組件時)最快 (JIT 可自由優化)

    若類別**只有靜態欄位初始化器**,不寫 static Class() {},CLR 標記 BeforeFieldInit,允許在任何時間 (甚至第一次存取前很久) 初始化,效能最佳但語義較鬆。需精確控制初始化時機 (如單例、資源獲取) 時,請顯式宣告空的靜態建構函式。

    常見陷阱

    1. 靜態建構函式拋出例外:該類別在當前 AppDomain **永久不可用** (後續存取擲 TypeInitializationException),不可恢復。避免在靜態建構函式做可能失敗的 IO/網路操作,或包裝 try/catch 並設定降級值。
    2. 循環相依導致資料不一致:如上例,設計時避免跨類別靜態欄位互相引用。
    3. 靜態建構函式中存取實例成員:編譯錯誤 (CS0120),靜態上下文無 this。
    4. 繼承誤區:子類別不會自動呼叫父類別靜態建構函式,各自獨立觸發。

    最佳實務模式:單例

    
    public sealed class Singleton
    {
        private static readonly Singleton _instance = new Singleton();
        public static Singleton Instance => _instance;
    
        // 顯式靜態建構函式 -> 精確語義、防止 BeforeFieldInit 過早初始化
        static Singleton() { }
    
        private Singleton() { /* 私有建構函式 */ }
    }
    

    總結

    • 靜態建構函式 = **精確、執行緒安全、一次性** 的靜態初始化入口。
    • 搭配 static readonly 實現不可變靜態資料。
    • 避免循環相依、避免例外、理解 BeforeFieldInit 與顯式靜態建構函式的效能/語義取捨。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved