1. 常數
- 值必須在編譯期確定,嵌入組件中繼資料,故必須為基元類型。
- 屬於型別一部分,視為靜態成員(不顯式宣告
static)。
- 編譯時從中繼資料提取值嵌入 IL,故常數無位址、無記憶體配置、不可透過引用傳遞。
- 跨組件版本控制缺陷:引用外部 DLL 時常數值已嵌入自身 IL,刪除該 DLL 不影響執行;該 DLL 修改常數值重編譯,自身專案仍用舊值,唯有重編譯自身專案才能取得新值。
2. 欄位
- 區別於常數:傳遞引用,可為任何資料類型。
- 分靜態欄位(型別載入 AppDomain 時建立,即首次引用型別/JIT 編譯時)與實例欄位(建立實例時配置動態記憶體)。
readonly (IL: InitOnly):僅能在建構函式寫入,其他地方唯讀(反射可修改)。
volatile:見線程同步筆記。
- 基於引用,不直接嵌入 IL 中繼資料,編譯含欄位的 DLL 會影響使用該 DLL 的專案,實現版本控制(永遠使用最新 DLL)。
- 多數欄位使用內聯初始化(CLR 建議),但內聯中不可使用方法/屬性。內聯初始化在
ctor/cctor 之前。
readonly 引用類型欄位的語意
標示引用不可變,而非物件不可變。範例:
public static readonly Char[] InvalidChars = new Char[] { 'A', 'B', 'C' };
// 合法:修改陣列元素
InvalidChars[0] = 'X';
// 非法:修改引用本身
InvalidChars = new Char[] { 'X', 'Y', 'Z' };
補充
- 實例方法可使用靜態變數。
- 靜態方法不可使用實例變數。