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

C# 靜態建構函數與抽象類別建構函數面試經典陷阱解析

     編輯:關於C#

陷阱一:靜態建構函數觸發時機


class Base
{
    static Base() { Console.WriteLine("Static Base"); }
    public Base() { Console.WriteLine("Base"); }
}

問題:`new Base()` 輸出什麼?

錯誤直覺:只輸出 "Base" (以為靜態建構函數不會被觸發)。

正確輸出:

Static Base
Base

規則 (依據 ECMA-334 / MSDN):

  1. 靜態建構函數在**第一次存取該類別的任何靜態/實例成員前**自動由 CLR 呼叫一次。
  2. 建立實例 (`new Base()`) 屬於存取實例成員,故先觸發靜態建構函數。
  3. 僅宣告變數 `Base b = null;` **不會**觸發。

繼承鏈中的呼叫順序


class Child : Base
{
    static Child() { Console.WriteLine("Static Child"); }
    public Child() { Console.WriteLine("Child"); }
}

`new Child()` 輸出:

Static Child
Static Base
Base
Child

順序口訣:子類靜態 → 父類靜態 → 父類實例 → 子類實例 (與實例建構函數順序相反)。

陷阱二:抽象類別可以有建構函數嗎?


abstract class A
{
    public A() { PrintFields(); }
    public virtual void PrintFields() { }
}

class B : A
{
    int x = 1;
    int y;
    public B() { y = -1; }
    public override void PrintFields() => Console.WriteLine($"x={x}, y={y}");
}

問題:`new B()` 輸出什麼?

常見誤區:抽象類別不能實例化,所以不能有建構函數 / 編譯錯誤。

事實:抽象類別可以且通常應定義建構函數**,供衍生類別呼叫初始化基底狀態。

執行流程:

  1. `new B()` → 呼叫 `B()`
  2. `B()` 隱式呼叫 `base()` → 執行 `A()`
  3. `A()` 呼叫 `PrintFields()` → 多型調度至 `B.PrintFields()`
  4. 此時 `B` 的欄位初始化 (`int x = 1`) **已完成**,但 `B()` 建構函數本體 (`y = -1`) **尚未執行**
  5. 輸出:x=1, y=0 (y 為預設值 0)

核心教訓

  1. 靜態建構函數:由 CLR 保證在第一次使用類別前執行一次,執行緒安全,無法手動呼叫。
  2. 抽象類別建構函數:專為衍生類別初始化基底狀態設計,**避免在其中呼叫虛擬方法**,因衍生類別欄位可能尚未初始化。
  3. 建構函數呼叫虛擬方法:極高風險,破壞物件初始化順序不變項,除非極度確定衍生類別不依賴欄位初始化。

面試加分回答

「靜態建構函數由 CLR 在類別首次被使用前自動觸發,繼承鏈中子類靜態建構函數優於父類。抽象類別擁有建構函數供衍生類別鏈式初始化,但在建構函數中呼叫虛擬方法會導致衍生類別欄位未初始化即被使用,屬於危險模式,應避免。」

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