對於常量,C#裡有兩個不同的版本:運行時常量和編譯時常量。
因為 他們有不同的表現行為,所以當你使用不當時,將會損傷程序性能或者出現錯誤 。
兩害相權取其輕,當我們不得不選擇一個的時候,我們寧可選擇一個 運行慢一點但正確的那一個,而不是運行快一點但有錯誤的那個。基於這個理由 ,你應該選擇運行時常量而不是編譯時常量(譯注:這裡隱藏的說明了編譯時常 量效率更高,但可能會有錯誤)。
編譯時常量更快更直接,但在可維護性 上遠不及運行時常量。保留編譯時常量是為了滿足那些對性能要求克刻,且隨著 程序運行時間的過去,其值永遠不發生改變的常量使用的(譯注:這說明編譯時 常量是可以不被C#采用的,但考慮到性能問題,還是做了保留)。
你可以 用關鍵字readonly來聲明(declare)一個運行時常量,編譯時常量是用關鍵字 const聲明的。
//Compile time constant:
public cocnst int _Millennium = 2000;
//Runtime constant:
public static readonly int _ThisYear = 2007;//
(譯注:原文為2004)
編譯時常量與運行時常量不同之處表現在如何對他們的訪問上。
一個編譯時常量會被目標代碼中的值直接取代。下面的代碼:
if (myDateTime.Year == _Millennium)
會與下面寫的代碼編譯成完 全相同的IL代碼:
if(myDateTime.Year == 2000)
運行時常量的值是在運行時確定的。當你引用一個只讀常量時(read-only)IL會 為你引用一個運行時常量的變量,而不是直接使用該值。
當你任意的使 用其中一個常量時,這些區別就在一些限制上表現出來。編譯時常量只能是基本 類型(primitive types)(built-in integral and floating-poing types),枚舉 或者是字符串。這些就是你只能給運行時常量在初始化時賦值的類型。這些基本 類就是可以被編譯器在編譯IL代碼時直接用真實的值所取代的數據類型。下面的 代碼塊(construct)不能通過編譯。你不能用new運算符初始化一個編譯時常量, 即使這個數據類型是值類型。
//Does not complIE, use readonly instead:
private const DateTime _classCreation = new DateTime (2000,1,1,0,0,0);
(譯注:DateTime是一個值類型數據,但上面的代碼 因為用了new運算符,編譯器無法在編譯確定具體的對象應該用什麼樣的實際值 來取代,所以無法通過編譯。)
編譯時常量僅限於數字和字符串。只讀變 量,也就是運行時常量,在構造函數(constructor)執行完成後它們是不以能被 修改的。但只讀變量是所有不同的,因為他們是在運行時才賦值的。當你使用運 行時常量時,你有更大的可伸縮性。有一點要注意的是,運行時常量可以是任何 類型的數據。而且你必須在構造函數裡對他們初始化,或者你可以用任何一個初 始化函數來完成。你可以添加一個DateTime結構的只讀變量(--運行時常量),但 你不能添加一個DateTime結構的(編譯時)常量。
你可以把每一個實例(的 常量)指定為只讀的,從而為每一個類的實例存放不同的值。與編譯時常量不同 的是,它只能是靜態的。
(譯注:簡單的講,運行時常量可以是一個類的 實例成員,也可以是一個類型的靜態成員,而編譯時常量只能是靜態成員,因此 類似:static const string m_name;的代碼是不能通過編譯的。)
只讀 數據最重要的區別是他們在運行時才確定值。當你使用只讀變量 時,IL會為你 產生一個對只讀變量引用,而不是直接產生數值。隨著時間的推移,這個區別在 (系統)維護上有深遠的潛在影響。
編譯時常量生成的IL代碼就跟直接使 用數值時生成的IL是一樣的,即使是在跨程序集時:一個程序集裡的編譯時常量 在另一個程序集會保留著同樣的值(譯注:這裡說的不是很清楚,看後面的這個 例子可能會更清楚一些)。
編譯時常量和運行時常量的賦值方法對運行時 的兼容性有所影響。
假設你已經在程序集Infrastructure中同時定義了 一個const和一個readonly變量:
public class UserfulValues {
public static readonly int StartValue = 5;
public const int EndValue = 10;
}