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

C# 類成員初始化順序(二):繼承鏈實例與靜態初始化實測

     編輯:關於C語言

測試代碼結構


class BaseA
{
    public static string StaticField = "BaseA靜態";
    public string InstanceField = "BaseA實例";
    static BaseA() { Console.WriteLine("基類靜態構造"); }
    public BaseA() { Console.WriteLine("基類構造函數"); }
}

class DerivedB : BaseA
{
    public static string StaticField = "DerivedB靜態";
    public string InstanceField = "DerivedB實例";
    static DerivedB() { Console.WriteLine("派生類靜態構造"); }
    public DerivedB() { Console.WriteLine("派生類構造函數"); }
}

static void Main()
{
    var obj = new DerivedB();
}

實際輸出順序


派生類靜態構造
派生類實例字段初始化
基類靜態構造
基類實例字段初始化
基類構造函數
派生類構造函數

順序規律總結

關鍵風險:派生類字段初始化器引用基類成員


class Base { public int Value = 10; }
class Derived : Base
{
    public int DoubleValue = Value * 2; // 風險:此時基類字段已初始化,但基類構造函數未執行
}

雖然基類字段初始化器已執行(值為 10),但基類構造函數邏輯尚未運行,若構造函數修改 Value 會導致不一致。

對比 Java

  • Java:靜態初始化基類→派生類;實例字段初始化基類→派生類;構造函數基類→派生類。完全自上而下。
  • C#:靜態初始化觸發類優先;實例字段初始化派生類優先。設計考量:支持派生類字段初始化器使用基類靜態成員,但引入了上述風險。

記憶體分配與默認值

  • new DerivedB() 先在托管堆分配內存,所有實例字段零初始化(0/null/false)。
  • 然後依次執行派生類字段初始化器 → 基類字段初始化器 → 基類構造函數 → 派生類構造函數。

C# 無構造函數初始化列表

不支持 Base(int v) : value(v) {} 語法,因為分配與初始化嚴格分離,且需支持反射創建實例(Activator.CreateInstance)。

下一篇預告

探討兩類相互引用靜態初始化的死鎖風險與解決方案(Lazy<T>、靜態構造函數延遲)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
階段順序說明
靜態初始化派生類 → 基類首次訪問派生類觸發,基類靜態構造在派生類靜態構造之後
實例字段初始化派生類 → 基類字段初始化器按繼承鏈從派生到基類執行
構造函數執行基類 → 派生類構造函數從基類到派生類執行