WisdomSoft - for your serial experiences.

定数と読み取り専用フィールド

名前付きの固定値を表現する方法を解説します。代表的な数学定数である円周率のように、変化しない値に対して識別子を設定できます。

固定された値

数学における円周率 π のような固定された値は定数(constant)として宣言できます。定数は、コンパイル時に確定できる固定された値に名前を付けたものです。円周率 π は 3.14159265... という浮動小数点数リテラルで指定できますが、数値には意味が書かれていません。繰り返し利用する値は定数として用意するべきです。

定数は変数と異なり、プログラム中で不変です。定数はリテラルの別名であり、変数のように異なる値を設定することはできません。

定数を宣言するには const キーワードを用いて定数宣言(constant-declaration)をメンバに記述します。

定数宣言
定数修飾子 const  定数名 = 定数式 ;

定数修飾子にはアクセス修飾子など、この定数の性質を設定する修飾子を指定します。その後 const キーワードに続いて定数の型を指定します。定数の型はコンパイル時に確定できる値でなければなりません。よって int や bool などの基本データ型、または文字列型を指定することになります。任意の参照型を定数にすることも可能ですが、インスタンスはコンパイル時に作成できないため null 以外を定数とすることはできません。

定数名には、他の識別子と同じ命名規則で定数を識別する名前を指定します。慣例的に定数名は全て大文字になります。定数は = 記号に続く定数式で固定されます。定数式はコンパイル時に値を評価できなければならないため、数値、文字、文字列などのリテラルで組まれた式のみが許されます。オブジェクト生成式を定数にすることはできません。

public const double PI = 3.141592;

定数メンバは静的メンバと同様にアプリケーション全体で単一のグローバルな値であり、インスタンスは持っていません。よって、静的メンバと同様にクラス名からメンバアクセスします。

コード1
class MagicalGirl
{
    public const string MAGIC_WORD = "ぴぴるぴるぴるぴぴるぴ~";
}

class Test
{
    public static void Main(string[] args)
    {
    	//MagicalGirl.MAGIC_WORD = "ピーリカピリララ ポポリナペーペルト"; //エラー
        System.Console.WriteLine(MagicalGirl.MAGIC_WORD);
    }
}
実行結果
コード1 実行結果

コード1の MagicalGirl クラスには定数メンバ MAGIC_WORD が宣言されています。この値は変数ではなく、コンパイル時に確定できるリテラルの別名でしかないため、代入演算で状態を変更することはできません。定数の値を変更しようとした場合はコンパイルエラーになります。

変更できないフィールド

定数は宣言時に値が確定していなければなりません。しかし、例えばスクリーン解像度など、 一度取得すれば定数的に扱える情報もあるでしょう。実行時まで確定できないが、初期化後は不変な値です。

このような場合、定数ではなく読み取り専用フィールド(readonly field)を用います。読み取り専用は定数ではなく変数ですが、初期化した後は変更はできません。読み取り専用フィールドは初期化子またはコンストラクタで初期化します。

読み取り専用フィールドを宣言するには readonly キーワードをフィールド宣言の修飾子として指定します。例えば、以下の value フィールドは読み取り専用フィールドであり、初期化以降は変更できません。

public readonly int value = 481;

読み取り専用は定数とは異なりインスタンスを持つことができます。 そのため、定数のように静的メンバとして扱いたい場合は静的フィールドに指定し、静的コンストラクタで読み取り専用フィールドを初期化するという方法が取られます。

読み取り専用フィールドは、フィールドの初期化子、またはコンストラクタでのみ値を代入できます。初期化子とコンストラクタ内であれば、何度でも変更できます。

class A
{
	public readonly int value = 0;
	public A()
	{
		value = 10;  //OK
		value += 10; //OK
	}
}

上のコードの value 読み取り専用フィールドは、初期化子とコンストラクタで値を変更しています。読み取り専用フィールドはコンストラクタ内であれば通常のフィールドと同じように代入で書き換えできます。

コード2
class MagicalGirl
{
    public readonly string Name;
    public MagicalGirl(string name)
    {
        this.Name = name;
    }
}

class Test
{
    public static void Main(string[] args)
    {
        MagicalGirl nanoha = new MagicalGirl("高町なのは");
        //nanoha.Name = "星野くらら"; //エラー

        System.Console.WriteLine("Name=" + nanoha.Name);
    }
}
実行結果
コード1 実行結果

コード2の MagicalGirl クラスは Name 読み取り専用フィールドを宣言しています。このフィールドはコンストラクタで初期化され、それ以降は変更できません。Main() メソッド内で Name フィールドの値を書き換えようとした場合はコンパイルエラーになります。