class Foo { public Foo(string s) => Console.WriteLine($"Foo ctor: {s}"); }
class Base { readonly Foo f = new Foo("Base init"); public Base() => Console.WriteLine("Base ctor"); }
class Derived : Base { readonly Foo f = new Foo("Derived init"); public Derived() => Console.WriteLine("Derived ctor"); }
new Derived();
Foo ctor: Derived init Foo ctor: Base init Base ctor Derived ctor
**保證建構子執行時,所有欄位皆已初始化完成**。
// 反例:若順序為 Base ctor → Derived 欄位初始化
class Base {
public Base() {
if (this is Derived d) d.DoSomething(); // 此時 Derived 欄位尚未初始化!
VirtualMethod(); // 多型呼叫衍生類別實作,存取未初始化欄位 → NullReference
}
protected virtual void VirtualMethod() {}
}
class Derived : Base {
readonly Foo _foo = new Foo("init");
public void DoSomething() => _foo.Bar(); // 爆炸
}
現行順序下,`Derived._foo` 在 `Base.ctor` 執行**前**就已就緒,虛擬方法呼叫安全。
class Base {
public Base() => Initialize(); // 危險!
protected virtual void Initialize() => Console.WriteLine("Base init");
}
class Derived : Base {
private readonly List _data = new List { 1,2,3 };
protected override void Initialize() => Console.WriteLine($"Derived init, Count={_data.Count}");
}
new Derived();
// 輸出: Derived init, Count=3 (欄位已初始化,安全)
// 但若 Derived 欄位初始化邏輯依賴 Base 狀態,則可能邏輯錯誤
鐵律:**建構子絕不呼叫虛擬方法** (或標記 `sealed override` / 私有方法)。
class Base {
public readonly int X;
public Base() { X = 10; Console.WriteLine($"Base ctor: X={X}"); }
}
class Derived : Base {
public readonly int Y = 20; // 欄位初始化
public Derived() { Y = 30; Console.WriteLine($"Derived ctor: Y={Y}"); }
}
new Derived();
// 輸出:
// Base ctor: X=10
// Derived ctor: Y=30
// 欄位初始化 Y=20 發生在 Base ctor 之後、Derived ctor 本體之前
// 建構子賦值 Y=30 覆寫欄位初始化值
| 階段 | 動作 | 順序 |
|---|---|---|
| 1 | 衍生類別 `readonly`/欄位初始化器 | 由衍生至基底 |
| 2 | 基底類別建構子 (含 `base()`) | 由基底至衍生 |
| 3 | 衍生類別建構子本體 (含 `this()` 遞迴) | 由基底至衍生 |
深刻理解此順序,能避免「建構子中存取未初始化欄位」、「虛擬方法呼叫陷阱」、「readonly 語意誤解」等經典 Bug,寫出更健壯的類別繼承體系。