C#におけるconst,readonly,static readonlyの特徴と使い分ける方法
こんにちは!ジェイです。今回はC#で、なんとなく使ってそうなconst,readonly,static readonlyについて深堀りしていこうと思います。
どんな時に使うか?
主に値を変えたくない定数として、宣言する時に使います。定数とは、一度値が代入されてから変更されない事が保証されている値の事です。定数には、数字、bool、文字列などのプリミティブ型などが含まれます。
プログラミングを書いていると、マジックナンバーを避けるためや、その数の意味をはっきりさせるために定数をよく使います。
constについて
コンパイル時に値が埋め込む定数を宣言できます。コンパイル時定数は、コードがコンパイルされる時点で計算されて値が決定されると、企画によって定められた定数です。なので定数の方はコンパイル時に確定できる値でなければなりません。
主に固定長配列のサイズや列挙型の定義などに使用します。
constの特徴
- constは固定の値が埋め込まれるため、インスタンスが異なっても常に同じ値となり、インスタンスごとに値を格納する必要がないので暗黙的にstaticとなる。
- ローカル変数にも使える。
- 宣言時にのみ1度だけ初期化ができる。
- 埋め込みなので実行ファイルサイズは大きくなるが実行速度は速い。
- switchやデフォルト引数で使用可能。
- 静的に生成されているのでクラス名からしか取れない。
- コンパイル時定数はコードがコンパイルされる際、定数の値と同じ値に置き換えらえるので、結果としてリテラルを直接書いたものと同様になる。なので単純な計算の代入式は問題ないが、メソッドの実行を伴うような値の代入は許されない。
class Sample
{
public const double PI = 3.14; // OK
public const double piyo = PI * PI; // OK
public const double payo = Math.Sqrt(10); // NG
void Piyo(){
//コンパイルで生成される中間言語では下の条件式はmyData == 3.14となる
if(Moge == PI)
//処理
}
メソッドのデフォルト値
メソッドのデフォルト値もconstと同じ挙動になります。
public static void SomeThing(int arg = 100)
{
}
public static void Piyo ()
{
// 実際にはコンパイル時にHoge(100);となっている
SomeThing();
}
この様な場合には、オーバーロードを使って複数のメソッドを定義した方が良いです。
readonly
実行時に値を取得する読み取り専用の変数を宣言できます(実行時定数扱い)。
定数は宣言時に値が確定している必要があります。
実行時まで確定はできないが、初期化後は不変な値となります。定数と意味合いも似ていますが、定数ではなく読み取り専用な変数なので特徴も異なります。
readonlyの特徴
- readonlyは実行時に値が決定するので、必ずしも同じ値になるとは限らない。つまり、デフォルトではstaticとして宣言されていないのでインスタンスの中にある変数の一つであると見なすことができる。
- 宣言時もしくはコンストラクタ内で初期化・変更が可能。
- ローカル変数には使えない(クラスのメンバ変数のみ)。
- 普通の変数と同等の扱いをされる。変数は共有されるのでconstに比べるとサイズは小さい。
- switchやデフォルト引数で使用不可。
- 埋め込みではないのでアセンブリを差し替えればロードし直され、変更した値が使用される。
- 実行時定数は実行時に評価されるため、値そのものではなくreadonly変数を参照する中間言語(IL)が生成される。つまりconstのようにリテラルに置き換わらない。
- インスタンスをnewした結果やメソッドの実行を伴うような値を割り当てられる。
- .NETではJIT(Just In Time Compile)が行われるので、ソースコード中で値の変わる変数に見えても実質的に値が変わらないようなら定数としてコンパイルが行われる。
- C#7.2からは構造体自体にreadonlyをつけられるようになった。readonlyの構造体のフィールドも全てreadonlyとしなければならない。また、thisもreadonly扱いになる
class Sample
{
public readonly int Piyo;
public readonly Dog taro;
Sample(int huga)
{
//コンストラクタ内で書き換え可能
this.Piyo = huga;
this.taro = new Dog();
}
void Func(int mohu)
{
int muga = this.Piyo;
// 書き込み不可でエラー
this.Piyo = mohu;
}
}
static readonly
定数値が必要だが、その値の型がconst宣言では使用できない場合、またはその値をコンパイル時に計算できない場合はstatic readonlyフィールドが役に立ちます。
Effective C#では、バージョニング問題の観点からconstよりもstatic readonly
の使用が推奨されています。
まとめ
readonlyは読み取り専用であることを表明するのに使用すると良いです。
constは対象がprivateである場合、又は高いパフォーマンスが求められていて、 なおかつ将来にわたって変更されることがないことが明らかな場合にのみ使用するべきでしょう。
そうでなく、将来変更される可能性のある値を定数として公開する場合にはstatic readonlyを使うべきです。
アイコンは鬼塚@取締役さんからお借りしました