在探討成員初始化語句之前,我們先釐清物件的建構流程。當類型的第一個實例被建立時,執行順序大致如下:
對於同類型後續建立的物件,前四個步驟會被略過,直接從第 5 步開始。
成員初始化語句在建構函式之前執行,因此能更早完成初始化,方便後續使用;此外,它能避免在多個建構函式中重複撰寫初始化碼,特別是新增欄位時,將初始化邏輯放在欄位宣告處,可減少因建構函式不一致導致某些成員未被初始化的風險。程式碼也因此更簡潔易讀。
public class MyList
{
// 成員初始化語句
private ArrayList _list = new ArrayList();
}
雖然成員初始化語句很好用,但並非萬能。以下三種情況應避免使用:
這並非錯誤,但屬於多餘操作。根據建構流程,成員在配置記憶體時已自動以 0 初始化,顯式指派只會增加指令、降低效能。
若在成員初始化語句中建立物件,又在建構函式中依參數重新指派,會產生短暫的暫存物件,增加 GC 壓力。例如:
public class MyList
{
private ArrayList _list = new ArrayList(); // 成員初始化
public MyList() { }
public MyList(int nSize)
{
_list = new ArrayList(nSize); // 重新指派,前一個 ArrayList 成垃圾
}
}
較佳做法是移除成員初始化,改在各建構函式中統一處理:
public class MyList
{
private ArrayList _list;
public MyList() { _list = new ArrayList(); }
public MyList(int nCount) { _list = new ArrayList(nCount); }
}
成員初始化語句無法包裹 try-catch,例外會直接向外傳播,迫使呼叫端處理,導致呼叫端代碼繁瑣。此時應改用建構函式,在內部捕獲並處理例外:
public class MyTest
{
private TempObject _value;
public MyTest()
{
try { _value = new TempObject(); }
catch (Exception err) { /* 內部處理 */ }
}
}
成員初始化語句能提升程式碼清晰度並減少重複,但需避開上述三種情境。筆者個人傾向使用建構函式初始化,並透過建構函式鏈結(this())解決多建構函式的重複問題,這純屬個人偏好,專案可依團隊規範決定。