April 14, 2013

Effective C# - Prefer readonly to const

Effective C#第2版中的Prefer readonly to const章節中建議開發人員使用readonly取代const的常數宣告方式,如
public const string ConstVersion = "2.0";
改用
public static readonly string ReadonlyVersion = "2.0";
在此章節還有幾個要點
  1. 用const宣告常數的方式稱為compile-time constant,而用readonly的方式稱為runtime constant
  2. compile-time constant會在編譯程式碼時就被嵌入IL中,而runtime constant是在執行期時透過readonly變數來取得實際的常數值
  3. compile-time constant的效能比runtime constant來得好一點,因為它是在編譯期就被嵌入IL中
  4. compile-time constant是binary incompatible,也就是當compile-time constant的值有變動時,參考它的程式碼也要重新編譯及佈署,新的常數值才會被嵌入到IL中;runtime constant是在執行期時透過readonly變數來取得實際的常數值所以是binary compatible,因此在維護的彈性上runtime constant較佳
  5. 在使用具名參數(named parameter)或選擇性參數(optional parameter)時需要注意上述binary compatibility的問題
以下為簡單的測試範例

建立一library專案ConstvsReadonly.Const並新增下方類別。ConstVersion為compile-time constant,而ReadonlyVersion為runtime constant
public class Constants
{
    public const string ConstVersion = "1.0";
    public static readonly string ReadonlyVersion = "2.0";
}

建立一console app專案ConstvsReadonly,參考並新增下方類別
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Const:{0}", Constants.ConstVersion);
        Console.WriteLine("Readonly:{0}", Constants.ReadonlyVersion);
        Console.ReadKey(true);
    }
}

編譯、佈署並執行console app,可以看到如下結果


透過ILSpy檢視console app的執行檔可以看到Constants.ConstVersion已被置換為常數值1.0


接下來將Constants.cs修改成下方並重新編譯程式及佈署ConstvsReadonly.Const專案
public class Constants
{
    public const string ConstVersion = "2.0";
    public static readonly string ReadonlyVersion = "3.0";
}

再次執行console app可以看到以下結果


可以看到雖然重新佈署了ConstvsReadonly.Const專案,但Const的值保持不變(1.0)而Readonly的值更新了(3.0),這就是所謂的binary incompatible。我們重新佈署了修改的地方(ConstvsReadonly.Const專案),但因為ConstvsReadonly專案沒重新編譯佈署,造成嵌入至IL的ConstVersion常數值在兩個專案中不相同。

No comments: