這個做法介紹了成員初始化的另一種做法,一般來說都是常用建構函式來初始化成員,不過一個類別通常會有超過一個建構函式, 在這種情況下時間一長就很容易造成每個建構函式有細微的區別產生不同步的情況。
要避免這種情況最好是直接在聲明的時候就直接初始化(Initializers),不要等建構函式運行在去賦值。
public class MyClass
{
private List<string> labels = new List<string>();
}
使用這種寫法不管以後新增多少成員都可以正確初始化,就不用到每個建構函式額外新增初始化的程式。
但需要注意成員 Initializers
的執行順序是比 Constructors
還優先的。
有三種情況不應該使用 Initializers
來初始化成員:
- 要將成員初始化成 0 或 null 時。
- 已經在建構函式初始化過的成員。
- 搭配 try/catch 初始化成員。
在初始化時不用特別使用 Initializers
MyValType myVal1;
MyValType myVal2 = new MyValType();
public struct MyValType
{
public int MyProperty { get; set; }
public string MyProperty1 { get; set; }
}
在編譯過後的程式碼會長的像下面這樣,debug
模式會編譯使用 default 來進行歸零,不過這個步驟在更底層也會自動處理歸零,
所以在 release
模式會把這段程式碼優化掉。
// debug
private MyValType myVal1;
private MyValType myVal2 = default(MyValType);
// release
private MyValType myVal1;
private MyValType myVal2;
在建構函式初始化過成員的例子來說
msg
會先被初始化為 Set by initializer
之後在執行 Constructors
把 msg
換成 Constructed in main
。
void Main()
{
var d = new B();
}
class B
{
private readonly string msg = "Set by initializer"; // 1
public B()
{
msg = "Set by constructor"; // 2
}
}
編譯後的 C# 大概會像下面這個形式,所以就結果來說在 B 的 Constructors
中 msg
被初始化了兩次,這樣導致第一個 Set by initializer
還沒有做事情就馬上被當成垃圾準備等 GC 進行回收,也可以看出 Initializers
是比 base..ctor()
還優先執行的這個特性。
B..ctor
msg = "Set by initializer";
base..ctor();
msg = "Set by constructor";
最後是搭配 try/catch 初始化成員,如果你初始化需要檢查例外錯誤就只能在建構函式中處理,Initializers
只能做基礎的初始化。
Summary
在這個做法中建議是在聲明的時候就直接初始化,使用 Initializers
,不僅寫起來比較直觀,特別是在有多個建構函式的情況底下也很方便閱讀跟維護。