這個做法介紹了成員初始化的另一種做法,一般來說都是常用建構函式來初始化成員,不過一個類別通常會有超過一個建構函式, 在這種情況下時間一長就很容易造成每個建構函式有細微的區別產生不同步的情況。

要避免這種情況最好是直接在聲明的時候就直接初始化(Initializers),不要等建構函式運行在去賦值。

public class MyClass
{
	private List<string> labels = new List<string>();
}

使用這種寫法不管以後新增多少成員都可以正確初始化,就不用到每個建構函式額外新增初始化的程式。 但需要注意成員 Initializers 的執行順序是比 Constructors 還優先的。

有三種情況不應該使用 Initializers 來初始化成員:

  1. 要將成員初始化成 0 或 null 時。
  2. 已經在建構函式初始化過的成員。
  3. 搭配 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 之後在執行 Constructorsmsg 換成 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 的 Constructorsmsg 被初始化了兩次,這樣導致第一個 Set by initializer 還沒有做事情就馬上被當成垃圾準備等 GC 進行回收,也可以看出 Initializers 是比 base..ctor() 還優先執行的這個特性。

B..ctor
    msg = "Set by initializer";
    base..ctor();
    msg = "Set by constructor";

最後是搭配 try/catch 初始化成員,如果你初始化需要檢查例外錯誤就只能在建構函式中處理,Initializers 只能做基礎的初始化。


Summary

在這個做法中建議是在聲明的時候就直接初始化,使用 Initializers,不僅寫起來比較直觀,特別是在有多個建構函式的情況底下也很方便閱讀跟維護。