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

深入解析 C# 類別成員初始化順序:靜態、實例與建構函式的執行時機

     編輯:關於C#

前言

C# 為純物件導向語言,物件生命週期包含配置記憶體、初始化、使用、銷毀四階段。理解初始化順序對撰寫高品質程式碼至關重要,特別是繼承體系中靜態/實例成員與建構函式的交互行為。本文以 C# 2.0 為例,原則適用於所有符合 CLS 規範的語言。

引用類型成員初始化實測

範例程式碼


class Program
{
    static void Main(string[] args) { DriveB d = new DriveB(); }
}

class BaseA
{
    static DisplayClass a = new DisplayClass("基類靜態成員初始化");
    DisplayClass BaseA_c = new DisplayClass("基類實例變數 BaseA_c 初始化");
    public BaseA() { Console.WriteLine("基類建構函式被呼叫"); }
}

class DriveB : BaseA
{
    static DisplayClass DriveB_b = new DisplayClass("衍生類靜態成員 DriveB_b 初始化");
    DisplayClass DriveB_c = new DisplayClass("衍生類實例變數 DriveB_c 初始化");
    public DriveB() { Console.WriteLine("衍生類建構函式被呼叫"); }
}

class DisplayClass
{
    public DisplayClass(string msg) { Console.WriteLine(msg); Console.WriteLine(); }
}

執行結果與順序結論

衍生類靜態成員 DriveB_b 初始化
衍生類實例變數 DriveB_c 初始化
基類靜態成員初始化
基類實例變數 BaseA_c 初始化
基類建構函式被呼叫
衍生類建構函式被呼叫

初始化順序總結:

  1. 衍生類靜態成員
  2. 衍生類實例成員
  3. 基類靜態成員
  4. 基類實例成員
  5. 基類建構函式
  6. 衍生類建構函式

關鍵觀察

  • 實例成員初始化由子類往父類(衍生→基底),但建構函式執行順序相反(基底→衍生)。
  • 因此在衍生類實例成員初始化時,不可參考基類成員,因基類成員尚未初始化。
  • C# 配置記憶體後會先將所有實例欄位設為預設值(0、null 等),再執行成員初始化器。不支援 C++ 風格的建構函式成員初始化列表。

靜態成員初始化時機與循環參考陷阱

延遲初始化特性

C# 靜態成員僅在首次存取該類別時初始化(BeforeFieldInit 語意),優點:減少啟動記憶體佔用、加快組件載入速度。

循環參考案例分析


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 的靜態欄位初始化:Y = A.X + 1。
  2. 存取 A.X 觸發 A 靜態建構函式:X = B.Y + 1。
  3. 此時 B.Y 尚未完成賦值(仍為預設值 0),故 X = 1。
  4. A 靜態建構函式結束,回到 B.Y 賦值:Y = A.X + 1 = 2。

結論:靜態初始化包含欄位宣告賦值與靜態建構函式,僅執行一次,循環參考時依賴預設值。

值類型 結構 的初始化差異

  • 靜態初始化與引用型別相同。
  • 結構不可繼承,實例初始化較單純。
  • 值類型不可為 null,宣告即配置記憶體並由 CLR 自動呼叫無參數建構函式完成預設初始化(所有欄位歸零)。
  • 開發者無法自訂無參數建構函式(被 CLR 佔用),只能多載有參數版本。
  • 良好習慣:使用前明確賦值,避免隱含預設值導致邏輯錯誤。

最佳實務建議

  • 實例成員初始化優先使用建構函式:執行順序基底→衍生,避免存取未初始化的基類成員;支援複雜邏輯與例外處理。
  • 簡單內建型別(int、enum、string)直接在宣告處初始化無妨。
  • 靜態成員利用延遲初始化特性,但小心循環參考。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved