C# 型別初始式(Type Initializer) .cctor

C# 型別初始式(Type Initializer) .cctor

大部分的人都知道可以為類別加上數個建構式(Constructor),在配置實體時會自動去呼叫。

但還有另外一個比較少用的,是型別的初始式,他是一個不能有參數且沒有回傳值的特殊Method

並且沒有辦法在程式裡面呼叫,只有CLR在第一次為型別初始化的時候才會呼叫的特殊方法。

 

寫法是

public class TestClass{
	static TestClass(){
		//...只有第一次初始化型別會呼叫
	}
}

一般我們都不會特別的去用他,但如果一個類別裡面有一些靜態的欄位,如:

public class TestCalss{
	public static string Name = "CodingRoad";
}

其編譯後的樣子,也會是像下面這樣:

public class TestClass{
	public static string Name;
	static TestClass(){
		Name = "CodingRoad";
	}
}

型別的初始化動作是在什麼時候執行的呢?

一般來說,CLR只保證初始化動作會在第一次存取該型別的靜態成員之前完成。

但在這個保證之下,初始化的時間還是有所區別,其差別是由metadata裡面的beforefieldinit這個attribute控制的

在沒有加入型別初始式的時候,在編譯時類別就會被加上beforefieldinit這個attribute

2011-08-08_005049

如果有加入的話則沒有

2011-08-08_005345

那有沒有這個attribute會有什麼樣的差別呢?

先來說沒有的時候好了,沒有beforefieldinit的時候,型別的初始式就是在

  • 第一次實體被配置時 或
  • 任何靜態成員被使用的時候

這兩種情況下會執行型別初始式。

但如果有加的時候,那可能的情況就複雜多了…

  • 在任何成員都還未使用的時候,就執行初始化動作 或
  • 延後型別初始化,直到有靜態欄位第一次存取時才執行

第二點的意思是,只要沒有使用到靜態欄位的情況下,型別初始化都不一定會執行。

聽起來有點複雜跟弔詭,而我自己實測的結果,也沒有測出第二種情況發生

但唯一可以確定的是,在使用靜態欄位前,初始化動作一定會執行完畢。

有一篇還不錯的文章,可以看 C# and beforefieldinit

 

另外在搜尋這方面的資訊時,有看到一個有趣的實驗,

如以下的Code,沒有加入型別初始式的時候

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("開始執行");

        //呼叫一個Method
        Method1();

        //呼叫靜態方法
        TestClass.GetString("呼叫靜態方法!");

        Console.ReadKey();
    }

    static void Method1()
    {
        Console.WriteLine("呼叫Method1");
        string s = TestClass.staticString;
    }
}

public class TestClass
{
    public static string staticString = GetString("初始化靜態欄位");

    public static string GetString(string s)
    {
        Console.WriteLine(s);
        return s;
    }
}

執行結果:

在Method1中,還沒碰到該類別的靜態欄位時(第19行),該欄位就已經被初始化了。

image

如果有自行加入型別初始化的時候

public class TestClass
{
    static TestClass()
    {
    }
    public static string staticString = GetString("初始化靜態欄位");

    public static string GetString(string s)
    {
        Console.WriteLine(s);
        return s;
    }
}

執行結果:

就是在第一次存取靜態欄位時,才會初始化

image

關於詳細的測試,可以看

关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释

对beforefieldinit的理解  這兩篇

 

如有任何問題,歡迎提出指教,謝謝。