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

C# 類成員初始化順序(三):循環靜態依賴與結構體初始化

     編輯:關於C語言

經典循環依賴案例


class A
{
    public static int X;
    static A() { X = B.Y + 1; }
}

class B
{
    public static int Y = A.X + 1;
    static B() { }
    static void Main()
    {
        Console.WriteLine($"X={A.X}, Y={B.Y}"); // 輸出:X=1, Y=2
    }
}

執行邏輯逐步拆解

  1. Main 在 B 中 → 首次訪問 B 觸發 B 靜態初始化。
  2. B 靜態字段聲明:Y = A.X + 1 → 需訪問 A.X。
  3. 首次訪問 A → 觸發 A 靜態初始化。
    1. A 靜態字段 X 無初始化器 → 默認 0。
    2. A 靜態構造函數:X = B.Y + 1 → 再次訪問 B.Y。
  4. B 正在初始化中,不會重入 靜態初始化 → B.Y 仍為默認值 0。
  5. A.X = 0 + 1 = 1,A 初始化完成。
  6. 回到 B 字段初始化器:Y = A.X + 1 = 1 + 1 = 2。
  7. B 靜態構造函數執行(空)。
  8. Main 繼續,輸出 X=1, Y=2。

核心規則總結

  • 靜態初始化 = 靜態字段初始化器(按文本順序) + 靜態構造函數。
  • 僅在首次訪問類時觸發,且線程安全(CLR 保證)。
  • 初始化過程中再次訪問同類 → 直接使用當前已初始化部分(可能為默認值),不重入。
  • 循環依賴導致「看到」對方未完全初始化的狀態,結果取決於代碼順序。

實例成員初始化順序

  1. 基類實例字段初始化器(按聲明順序)
  2. 基類實例構造函數
  3. 派生類實例字段初始化器
  4. 派生類實例構造函數

注意:字段初始化器在構造函數之前執行,且從基類到派生類。這與靜態初始化「首次訪問觸發」不同,實例初始化是確定性順序。

結構體特殊規則

  • 結構體是值類型,不允許聲明無參構造函數(CLR 保留用於默認初始化)。
  • 聲明結構體變量即自動零初始化(所有字段為 0/null/false),無需 new。
  • 可聲明帶參構造函數,但必須初始化所有字段(編譯器強制)。
  • 無繼承,無虛擬方法,初始化邏輯相對簡單。

最佳實踐建議

  1. 避免跨類靜態字段循環依賴;若必須,使用延遲初始化模式(Lazy<T> 或雙重檢查鎖)。
  2. 實例字段優先在構造函數初始化,邏輯集中、順序可控、支持異常處理。
  3. 簡單不可變類型(int、string、enum)字段初始化器可接受。
  4. 結構體字段聲明時務必賦值或調用帶參構造函數,避免使用未初始化的零值。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved