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

.NET 基類構造函數與欄位初始化器執行順序深度解析

     編輯:關於.NET

問題引入

子類實例化時,基類構造函數與子類欄位初始化器(private X x = new X();)誰先執行?許多開發者誤以為基類構造函數優先,實際情況令人驚訝。

實驗代碼與結果

public class A
{
    public A() { Console.WriteLine("A的構造函數被調用"); }
}

public class B : A
{
    private X x = new X(); // 欄位初始化器
}

public class X
{
    public X() { Console.WriteLine("X的構造函數調用"); }
}

static void Main(string[] args)
{
    B b = new B();
}

輸出結果

X的構造函數調用
A的構造函數被調用

結論:子類欄位初始化器先於基類構造函數執行!

IL 代碼揭秘

不使用初始化器(在構造函數中賦值)

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
    IL_0000: ldarg.0
    IL_0001: call instance void A::.ctor()      // 先調用基類構造函數
    IL_0006: nop
    IL_0007: nop
    IL_0008: ldarg.0
    IL_0009: newobj instance void X::.ctor()    // 再初始化欄位
    IL_000e: stfld class X B::x
    IL_0013: nop
    IL_0014: ret
}

使用欄位初始化器

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
    IL_0000: ldarg.0
    IL_0001: newobj instance void X::.ctor()    // 先初始化欄位
    IL_0006: stfld class X B::x
    IL_000b: ldarg.0
    IL_000c: call instance void A::.ctor()      // 再調用基類構造函數
    IL_0011: nop
    IL_0012: ret
}

深度原理解析

C# 編譯器將欄位初始化器的代碼插入到構造函數的開頭,在 base() 調用之前。這意味著:

  • 欄位初始化器不能依賴基類構造函數的初始化邏輯
  • 基類構造函數執行時,子類欄位已完成初始化
  • 這與 Java 行為一致(Java 也是先執行子類實例初始化器,再執行基類構造函數)

最佳實踐建議

  1. 避免在欄位初始化器中依賴基類狀態:基類構造函數尚未運行
  2. 複雜初始化邏輯移至構造函數:便於控制順序、異常處理與除錯
  3. 僅對不可變的簡單值使用初始化器:如 private readonly int _id = 42;
  4. 警惕虛方法調用:基類構造函數調用虛方法時,子類覆寫方法可能訪問未初始化的欄位

總結

.NET 中欄位初始化器執行於基類構造函數之前,這是由 C# 編譯器生成 IL 的順序決定的。理解此機制對避免初始化順序導致的隱蔽 Bug 至關重要。建議複雜初始化邏輯顯式放在構造函數中,保持代碼清晰可控。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved