子類實例化時,基類構造函數與子類欄位初始化器(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的構造函數被調用
結論:子類欄位初始化器先於基類構造函數執行!
.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() 調用之前。這意味著:
private readonly int _id = 42;.NET 中欄位初始化器執行於基類構造函數之前,這是由 C# 編譯器生成 IL 的順序決定的。理解此機制對避免初始化順序導致的隱蔽 Bug 至關重要。建議複雜初始化邏輯顯式放在構造函數中,保持代碼清晰可控。