C# 保證變數永遠有預設值
不同於 C/C++,C# 編譯器會為所有未明確初始化的欄位自動指派預設值,消除未初始化變數導致的未定義行為。
各型別預設值對照表
| 分類 | 型別 | 預設值 |
| 整數 | sbyte, byte, short, ushort, int, uint, long, ulong | 0 |
| char | '\0' (U+0000) |
| 浮點數 | float | 0.0f |
| double | 0.0d |
| decimal | 0.0m |
| 布林 | bool | false |
| 列舉 | enum | 0 (對應基礎型別零值) |
| 參考型別 | class, interface, delegate, array, string | null |
| struct | 各欄位遞迴預設值 |
初始化時序:靜態 vs 實例
class Demo {
// 靜態欄位:類別載入時初始化 (before first access)
static int _staticField = ComputeStatic();
static int _anotherStatic = _staticField + 1; // 可參考前面靜態欄位
// 實例欄位:每次 new 時初始化 (建構子執行前)
int _instanceField = ComputeInstance();
int _anotherInstance = _instanceField + 1; // 可參考前面實例欄位
// 建構子參數不可直接參考其他實例欄位 (尚未初始化)
// int _illegal = _instanceField + 1; // 編譯錯誤 CS0236
}
靜態建構子與靜態欄位初始化順序
- 依原始碼順序執行靜態欄位初始化運算式。
- 執行靜態建構子 (
static Demo())。
- 類別首次被存取前完成 (精確時機由 CLR 決定,
beforefieldinit 標記影響)。
實例建構時序
- 衍生類別建構子呼叫
base() (隱含或顯式)。
- 基類別實例欄位初始化運算式 (依宣告順序)。
- 基類別建構子本體執行。
- 依此遞迴至最衍生類別。
- 最衍生類別實例欄位初始化運算式。
- 最衍生類別建構子本體執行。
常見陷阱與解決方案
| 陷阱 | 原因 | 解決 |
實例欄位初始化運算式參考 this 或其他實例成員 | 欄位初始化在建構子之前,this 尚不可用 | 移至建構子本體初始化 |
| 靜態欄位相互參考導致循環依賴 | 初始化順序固定,後宣告者可見前宣告者 | 重構邏輯,或使用靜態建構子集中初始化 |
| 預設值非預期業務語意 (如 0 代表無效 ID) | 零值可能為合法業務值 | 使用 Nullable<T> 或自訂結構體封裝 |
readonly 與 const 的初始化差異
const:編譯期常數,IL 內嵌數值,必須在宣告時初始化,不可為參考型別 (string 除外)。
readonly:執行期常數,可在宣告或建構子初始化,支援任意型別,版本控制友善 (參考組件更新無需重新編譯消費者)。