.Net の 定数値・列挙値 は単なる置換

プログラミング .NET Framework 第3版 (マイクロソフト公式解説書)の第7章によると、.Net の定数値・列挙値は単なる置換処理だそうです。私は値が固定の変数があるとイメージしていたのですが実際は違うようです。定数・列挙値の動作と問題点について考えてみました。


■定数値・列挙値の処理について


値が固定の変数があるわけではなく、コンパイルされた時点で直接コードにリテラル値で埋め込まれます。C言語プリプロセッサで処理される #DEFINE と同じ動きです。イメージとしては以下のように処理されます。



const string CONST_PARAM = "Hello World";
Console.WriteLine(CONST_PARAM);


Console.WriteLine("Hello World");

中間言語(IL)になるとリテラルで直接コードに埋め込まれます。上記の例は1箇所でのみ定数値を使用していましたが、5箇所で使用すると5箇所に埋め込まれます。


const string CONST_PARAM = "Hello World";
Console.WriteLine(CONST_PARAM);
Console.WriteLine(CONST_PARAM);
Console.WriteLine(CONST_PARAM);
Console.WriteLine(CONST_PARAM);
Console.WriteLine(CONST_PARAM);


Console.WriteLine("Hello World");
Console.WriteLine("Hello World");
Console.WriteLine("Hello World");
Console.WriteLine("Hello World");
Console.WriteLine("Hello World");


■定数値・列挙値の特性・問題点


この事から以下の特性・問題点があると言えます。(すべて検証済。例は定数ですが列挙値についても同様です。)

  1. 直接コードに埋め込まれるため実行速度は速くなる。
  2. 複数個所にリテラル値が設定されるためプログラムサイズは大きくなる。
  3. 元の定数値・列挙値を変更しても、使用しているアセンブリをリコンパイルするまでは変更値は反映されない。
  4. 参照設定していても使用しているのが定数値・列挙値だけなら実行時にロードされることは無い。


定数値・列挙値を変更する場合は参照先も合わせてリコンパイルする必要があります。


■置換を行わない定数


定数と似た機能にクラスのReadOnly静的フィールドがあります。こちらは置換処理ではなく実行時に値が取得されます。クラスフィールドは値の初期化のタイミングが静的コンストラクタであるため、コンパイル時に値が置換されることはありません。定数と同じように使用することができます。



public static readonly string READONLY_FIELD = "25";


いずれにしても特性を理解しておくと問題を整理しやすくなります。