常量
常量的值永遠不會變化,因此總是被視為靜態成員。
const int someConst = 1;
在 ildasm 裡可以看到它會被標記成 literal:
someConst : private static literal int32
編譯期特性
- 常量值必須能在編譯期確定,編譯器會把它直接寫入程序集元數據。
- 因此,常量類型通常必須是編譯器能直接識別的基元類型。
- C# 也允許非基元類型的常量,但值只能是
null。
當代碼引用常量時,編譯器會去定義該常量的程序集元數據裡查值,然後把這個值直接嵌入產生出的 IL 裡。
這帶來三個結果:
- 運行時不需要為常量另外分配內存。
- 不能取得常量地址。
- 不能以傳引用的方式傳遞常量。
版本控制問題
常量在版本控制上其實很不方便。假設先有一個定義常量的 DLL:
public sealed class SomeLibraryType
{
public const int MaxEntriesInList = 1000;
}
然後有一個引用了這個常量的 EXE:
public sealed class Program
{
public static void Main()
{
Console.WriteLine("Max Entries supported in list: " +
SomeLibraryType.MaxEntriesInList);
Console.ReadLine();
}
}
在生成 EXE 時,編譯器不會等到運行時再去讀 DLL 裡的值,而是直接把常量值嵌進應用程序自己的 IL 中:
IL_0000: nop
IL_0001: ldstr "Max Entries supported in list: "
IL_0006: ldc.i4 0x3e8
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: call instance string [mscorlib]System.Int32::ToString()
IL_0013: call string [mscorlib]System.String::Concat(string,
string)
IL_0018: call void [mscorlib]System.Console::WriteLine(string)
這代表:
- 就算你之後重新修改並重新發佈 DLL,也不會影響已經編譯好的 EXE。
- 如果想讓應用程序吃到新的常量值,EXE 也必須重新編譯一次。
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter