| 特性 | const | readonly |
|---|---|---|
| 初始化時機 | 編譯期 (必須為常數運算式) | 運行期 (建構函式或宣告處) |
| 可用變數初始化 | 否 | 是 |
| 靜態性 | 隱含 static | 預設為實例成員,需顯式 static readonly |
| 型別限制 | 僅限值型別 + string + null | 任意型別 (含參考型別、結構、陣列) |
| 不同實例不同值 | 不可 (全程式共享) | 可 (每個實例可不同) |
| IL 標記 | literal | initonly |
| 跨組件版本風險 | 高 (值內嵌入呼叫端 IL) | 低 (運行期讀取欄位) |
public const double PI = 3.14159; // OK
public const int MaxSize = 100 * 1024; // OK: 常數運算式
// public const int Value = GetValue(); // 編譯錯誤: 非常數運算式
// public const MyClass Obj = new MyClass(); // 錯誤: 僅限 string/null
public readonly int InstanceId; // 實例唯讀
public static readonly string AppName; // 靜態唯讀
public MyClass()
{
InstanceId = Interlocked.Increment(ref _counter); // 運行期賦值
// InstanceId = 999; // 建構函式外賦值 → 編譯錯誤
}
static MyClass()
{
AppName = "MyApp " + DateTime.Now.Year; // 靜態建構函式賦值
}
public static readonly List Items = new List();
// 合法:修改集合內容
Items.Add("A");
Items[0] = "B";
// 非法:重新指派參考
// Items = new List(); // 編譯錯誤
// 結構體 readonly 則完全不可變
public readonly struct Point { public int X, Y; }
public static readonly Point Origin = new Point();
// Origin.X = 1; // 錯誤:readonly 結構體成員不可變
// 運行期初始化,支援複雜邏輯
private static readonly string DataPath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
private static readonly Lazy Resource =
new Lazy(() => new ExpensiveResource(), true); // 執行緒安全延遲初始化
組件 A 定義:
// Assembly A v1.0
public const int MaxConnections = 100;
public readonly int DefaultTimeout = 30;
組件 B 參考 A:
// Assembly B
Console.WriteLine(A.MaxConnections); // IL 內嵌為 100
Console.WriteLine(A.DefaultTimeout); // IL 為 ldsfld 讀取欄位
升級 A 為 v2.0:
// Assembly A v2.0
public const int MaxConnections = 200;
public readonly int DefaultTimeout = 60;
不重新編譯 B 直接替換 A.dll:
MaxConnections:B 仍輸出 100 (值已燒錄進 B 的 IL)DefaultTimeout:B 輸出 60 (運行期讀取新值)結論:公開常數謹慎使用 const,跨組件共享配置值請用 static readonly。
conststatic readonlyreadonlyreadonly、類別欄位全 readonly + 唯讀屬性public const 用於可能變更的業務配置