C# の const と static readonly の使い分け

C#でプログラム書いてるとコードを書いているときは、constとreadonlyの区別ってあんまりありませんよね。

ついつい定数は、constと書いてしまいます。

public static class HogeDefine
{
    public const uint MaxLength = 10;
    
    public static readonly uint DataSize = 64;
}

...

public void Main(string[] args)
{
    // constのほう
    for(int i = 0; i < HogeDefine.MaxLength; i++)
    {
        // 中略
    }
    
    // readonlyのほう
    byte bArray = new byte[HogeDefine.DataSize];
}

両方とも使い勝手は全く同じです。

が、これ、コンパイルした後に明確に違いが現れます。

コンパイル後は、以下のように変化してしまいます。

public void Main(string[] args)
{
    // const は、リテラルに置き換わってしまう
    for(int i = 0; i < 10; i++)
    {
        // 中略
    }
    
    // readonlyのほうは参照のまま
    byte bArray = new byte[HogeDefine.DataSize];
}

「constの方がリテラルの置き換わって」しまいます。

つまり以下条件を考慮して宣言をする必要があります。

1) constが定義されているDLL外から参照される可能性があり
2) 各々のDLLのリリースライフサイクルが違う

const の値が変更され、リンク先がコンパイルされない場合「参照先のDLLで古いconstの値が使用されっぱなし」という状況が発生します。

指定した乗数でない値が相手側から指定され、予期せず不具合が発生する場合があります。

この不具合の連絡が来ると

「10」が設計値なのに「12」になってる!!

どうして!?(コンパイラのバグ・・・?)

みたいな、わかる人にはわかるけど保守担当は事情を知らないので割と頭を抱えてしまう現象が発生したりします。


なので...

  • DLL外に公開する場合static readonly
  • 自分DLL内だけでしか使わない場合const

のスタンスで宣言したほうが安全かと思います。

本気でパフォーマンスが必要な場合constの方が実行効率はほんの少しだけconstの方がいいのでそういった場合もconstを指定することになります。
(まぁ、、、滅多にないと思いますが)


メソッドのデフォルト値もconstと同じ挙動になります。

public static void Foo(string str = "Default")
{
   // 省略

この場合呼び出し側で

public static void Main(string[] args)
{
    Hoge.Foo();
}

と呼び出して安心しててもコンパイル後に実際は

public static void Main(string[] args)
{
    Hoge.Foo("Default");
}

に置き換わってるので特に注意が必要です。
特に意識してない & 見えない & 便利なのでやりがちなんですが

こういったケースが困る場合、昔ながらの方法でオーバーロードを複数個定義したほうが幸せになれます。

まぁ配布しちゃったDLLの定数を後から変更するって事自体、何か計画性が無いとダメなんですがね・・・