這篇在講什麼
readonly 和 const 看起來都像是在說「不要改值」,但它們處理的是不同層級的問題。
這篇的重點是把兩者的使用時機拆清楚,避免在專案裡只是憑感覺選。
const 是什麼
const 代表編譯期就能確定的常數。
它有幾個特性:
- 宣告時就必須初始化
- 隱含為
static - 值會在編譯時直接嵌入使用端
例如:
public static class UsualString
{
public const string TagName = "TagName";
public const string BlinkSfxName = "BlinkSFX";
}
這很適合拿來放:
- 固定字串
- 數值常數
- 不依賴執行期狀態的設定值
const 的限制
因為它要求編譯期可確定,所以像下面這些都不適合:
DateTime.Now- 從設定檔讀進來的值
- 建構時才知道的資料
另外還有一個很實務的點:const 會把值嵌入引用端。
如果它是跨組件公開常數,之後你只重編定義端、不重編使用端,使用端可能仍然拿到舊值。
所以對外公開的常數,有時候反而更適合用 static readonly。
readonly 是什麼
readonly 修飾的是欄位。它允許你在以下時機賦值:
- 宣告當下
- 建構函式內
一旦初始化完成,後面就不能再改。
public class Enemy
{
public readonly Vector3 SpawnPos;
public Enemy(Vector3 spawnPos)
{
SpawnPos = spawnPos;
}
}
這很適合表示「建立後不應再變動」的狀態。
readonly 不等於物件完全不可變
這點很重要。readonly 保護的是欄位的引用不被重新指向,不代表被指到的物件內容一定不能改。
例如:
public readonly List<int> Values = new();
這代表:
- 你不能把
Values指向另一個List<int> - 但你仍然可以
Values.Add(1)
如果你追求的是更完整的不可變語意,還要搭配:
- immutable type
IReadOnlyList<T>- defensive copy
static readonly 介於兩者之間
static readonly 很常拿來和 const 比較,因為它也常用在類別共用資料上。
它的特性是:
- 仍然是類別層級共享
- 可以在執行期初始化
- 不會像
const那樣被直接嵌入引用端
例如:
public static class GameConfig
{
public static readonly Vector3 DefaultSpawnPoint = new(0f, 1f, 0f);
}
像 Vector3 這種不是編譯期常數的型別,就適合用 static readonly。
實務上怎麼選
可以用這個順序判斷:
- 如果值在編譯期就確定,而且永遠不變,用
const - 如果值在建構或初始化時決定,之後不該再改,用
readonly - 如果值是類別層級共用,但初始化要留到執行期,用
static readonly
我自己的整理
這三者的差異,不只是語法,而是你在對未來的維護者表達一件事:
- 這是永遠固定的值
- 還是只允許初始化一次的狀態
- 還是整個型別共享的執行期常量
用對這些修飾詞,可以直接降低很多不必要的狀態錯誤。
先記住的重點
const是編譯期常數,宣告時就要給值readonly是欄位只能在初始化階段賦值readonly不代表物件內容完全不可變- 跨組件公開常數時,要注意
const的值嵌入特性 - 需要執行期初始化的共享值,通常用
static readonly