定数 定義方法とバージョニング問題

基本文法

定数

C#言語にも#defineプリプロセッサはあるのですが、定数を定義するための用途では使われません
CやC++系の言語とは違うので注意です

const

C#で定数を表すにはconstキーワードを指定します
値を設定するのは宣言時のみです
コンパイル時定数、つまり、定数の値はコンパイル時に決まります

readonly

読取専用の修飾子ですが、定数の用途としても使えます
値を設定するのは宣言時またはコンストラクタで可能
実行時定数、つまり、プログラム実行時に値が参照されます
インスタンスフィールドで定義すると値が異なり得るので静的フィールドで定義する

getter property

Setterを持たないプロパティも定数としての用を足す

サンプルコード



    static class Constants
    {
        // const
        public const string JAPANESE_CAPITAL = "東京都";
        // readonly
        public static readonly string CURRENT_CAPITAL;
        // getter property
        public static string JAPANESE_CAPTIAL_EN => "TOKYO";

        // constructor
        static Constants() => CURRENT_CAPITAL = "とうきょうと";

    }

    class Sample
    {
        static void Main(string[] args)
        {
            string capital1 = Constants.JAPANESE_CAPITAL;

            string capital2 = Constants.CURRENT_CAPITAL;

            string capital3 = Constants.JAPANESE_CAPTIAL_EN;
        }
    }

バージョニング問題

constはコンパイル時定数です。そのため、あるライブラリで定義をし、これを参照したモジュールがある場合において、ライブラリ側の定数値を変更し参照モジュール側を再コンパイルせずに実行した場合、定数の値が更新されないという問題があります。
ソリューション内でプロジェクト参照している場合は大丈夫でしょうが、ライブラリとして提供している場合は問題となり得ます。
同様の問題は列挙体などにもあります。

定数の落穂

定数とは何か

定数とは、変数とは異なり変化しない値のことです。πの3.14159・・・だとかオイラーのγが0.57721・・・などですね。
地球の数はあと数十億年は変わりませんが、太陽系の惑星の数は微妙。社名や住所はどうでしょうか。

プログラミングにおいて定数とは、プログラムの実行中に変更されない値のことです。
社名や住所は大概の場合においては定数扱いでよいでしょう。変更時はプロセスの再起動が必要ですが。
それが許されない場合は変更を前提としたプログラムを組む必要があるので、要求仕様で確認しておきましょう。

その他の定義方法

定数を定義するには、基本的には上記3つの方法が一般的ですが、場合によっては列挙体を使う方が適切な場合があります。
何が適切なのかはケースバイケースですが、例えば都道府県名などが該当する場合があるでしょう。
但し列挙体では日本語対応の問題があります。列挙体の説明で触れる予定です。

即値(直値)と謎値

コード上の必要な個所に直接書き込まれた値を即値とか直値といいます。
その値が変更された場合、コード上の即値を全部探して全部修正して全部確認する手間が必要になります。
一方、定数を別途定義し、必ずそれを参照するよう実装されている場合は一か所の修正で済むという利点があります。

また、直接書き込まれた値は実装時にはそれが何の意味なのかが後でわからなくなる場合があり謎値(マジックナンバー)なんて呼ばれます。
他人の書いたコードが読みずらいのはこういうものが原因だったりします。定数定義は可読性の向上に資する場合もあります。