偶而遇到有人問 const
跟 static readonly
有什麼差別,如果是使用基本型別或字串,可能會感覺差不多。
大部分的人都會回答是賦予值的階段不同,const
是編譯時賦予值,static readonly
是執行時賦予值。
本篇將介紹 const
跟 static readonly
的差異。
基本介紹
const
1 | public const string ConstString = "ConstString - 1"; |
const
的特色:
- 只能在宣告時給值
- 無法修改內容
- 只能是基本型別或字串
readonly
1 | public static readonly string ReadonlyString = "ReadonlyString - 1"; |
readonly
的特色:
- 只能在宣告時或建構子給值
- 建構子執行完畢後,無法再修改內容
- 跟一般變數一樣可以是任意型別
宣告
static readonly
只能在靜態建構子修改內容
執行比較
我建了兩個範例專案,結構如下:
Example 是給 ExampleApp 參考使用,在 Example 新增一個 Sample.cs:
1 | namespace Example |
在 ExampleApp 中輸出這個常數:
1 | using System; |
Output 結果:
1 | ConstString = ConstString - 1 |
輸出就如程式邏輯一樣,看似 const
跟 static readonly
有什麼差別。
但實際上編譯出來的內容卻差很多,ExampleApp.Program.Main
經過編譯後產生的 IL 如下:
1 | .method private hidebysig static void Main(string[] args) cil managed |
如果有裝 .NET Reflector,可以看到編異後的 C# 程式碼:
Sample.ConstString
被轉換成"ConstString - 1"
。
乍看之下似乎覺得很合理,你可能心裡會想:
就編譯時賦予值啊!有什麼好說的!
但卻落入一個陷阱,假設更新 Example.dll 而不異動 ExampleApp.exe 的情況,會發生什麼事?
例如把 Example 的 Sample.cs 修改成下面內容:
1 | namespace Example |
程式依然能正常執行,但 Output 結果會是如下:
1 | ConstString = ConstString - 1 |
你可能會有點意外,但實際上就真的是編譯時賦予值啊!
ExampleApp 在第一次被編譯時,就已經把所有用到 const
的內容帶入其中,沒有重新編譯的情況下,const
的內容都不會改變。 而 static readonly
每次程式啟動都會去 Example 取得內容,所以 ExampleApp 就算沒有重新編譯,還是可以拿到 Example 更新後的值。
結論
static readonly
是比較建議的常數使用方法,可以比較彈性使用。const
建議的使用時機大概有兩種情況:
- 內容必須要在編譯時期決定時。
- 非常非常非常需要程式啟動效能時。