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
如果有加入的話則沒有
那有沒有這個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行),該欄位就已經被初始化了。
如果有自行加入型別初始化的時候
public class TestClass
{
static TestClass()
{
}
public static string staticString = GetString("初始化靜態欄位");
public static string GetString(string s)
{
Console.WriteLine(s);
return s;
}
}
執行結果:
就是在第一次存取靜態欄位時,才會初始化
關於詳細的測試,可以看
关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释
如有任何問題,歡迎提出指教,謝謝。