LoginSignup
20
16

More than 5 years have passed since last update.

C#-constとstatic readonlyの違い

Last updated at Posted at 2016-08-05

constもreadonlyも定数だけど、その挙動は大きく違う。
基本的にはstatic readonlyを使おう。
本当にその定数が将来的に変更されない(円周率PIとか)ならconstを使っても良い。

cosntって何?

  • const = コンパイル時定数
  • int,float,double,bool,char,stringの型が使える。
  • 宣言時に値を代入する必要がある。
  • string型が使えるので参照型も使えるのかも!と思ったけど、参照型でconstできるのはstringのみ
  • プロパティをconstで定義することはできない。
  • newを使えない
  • constを使った定数にstaticキーワードをつけることはできない。
  • static readonlyより早いらしい
  • コンパイルされる時に定数の値と同じ値に置き換えられる
  • switch文にも使える

Eric Lippertによれば、そもそもそのような状況はあってはならないもので、将来変更され得るような値をconst定義すべきではありません。円周率や金の原子に含まれる陽子の数のような、絶対不変の値のみがconst定義されるべきものである
http://ja.stackoverflow.com/questions/6653/const-と-static-read-only-の違いと使い分けについて

もう少しくだけて説明すると、const変数は自分が作ったアセンブリをクラスライブラリとして配布してしまうと、constの値を変えた場合は、それを使う側のアプリはリビルドしなければ反映されません。dll差し替えだけでは反映されないので注意。
http://ja.stackoverflow.com/questions/6653/const-と-static-read-only-の違いと使い分けについて

エラーはおきない
    public const string a = "1";
    public const string aa = null;//null代入はok
    public const int b = 1;
    public const bool c = true;
    public const float d = 0.0f;
    public const double e = 0.0d;
エラーが起きる
    public const string a; //宣言時に値が代入されていない!
    public const int b;//宣言時に値が代入されていない!
    const string a = new string ('a', 1); //newは使えない!
    public const Hoge hoge = new Hoge ();//参照型はstring以外は使えない
    public const int AAA {//プロパティは使えない
        get;
        set;
    }
    public const int? c = null;//null許容型はだめ
    public const string? d = null;//null許容型はだめ
    public const static int f = 1;//staticキーワードは使えない

また宣言時に計算処理を使って代入できる。ただし処理に使えるのは値かconst定数だけ!

宣言時に計算処理
    public const int a = 1;
    public const int b = a + 1;
    public const int c = b + 1;
宣言時に計算処理(宣言の順番は関係ない)
    public const int c = b + 1;//上の方にb+1しててもOK
    public const int a = 1;
    public const int b = a + 1;
エラーがでる
    public const int a = 1 + d; //dはconst定数じゃないから処理できない!
    public const int b = 1 + e; //eはconst定数じゃないから処理できない!
    public const int c = 1 + f; //fはconst定数じゃないから処理できない!
    int d = 10;
    public static readonly int e = 10;
    public readonly int f = 10;

もちろんフィールド,コンストラクタ,メソッド内で自由に宣言可能!
あとスコープが違えば名前がかぶっていてもOK

自由に宣言できる
public class Test
{
    const int a = 1; //名前が同じでもOK
    //コンストラクタ
    public Test ()
    {
        const int a = 1; //コンストラクタ内でも宣言OK
    }

    public void Method1 ()
    {
        const int a = 1; //メソッド内でも宣言OK
    }

    public static void Method2 ()
    {
        const int a = 1; //static メソッド内でも宣言OK
    }
}
switch文にも使える
        int b = 1;
        switch (b) {
        case a:
            break;
        case 2:
            break;
        default:
            break;
        }

constの速さ

結論:計測できず・・・

計測用コード
    void Start ()
    {
        check ();
    }

    const int a = 1;

    void check ()
    {
        int b;
        Profiler.BeginSample ("const");

        for (int i = 0; i < 100000; i++) {
            b = a;
        }
        Profiler.EndSample ();
    }

10万回constの値を取得した時に0.37ms ~ 0.5msほどかかった。
しかし、これだと変数bに代入する処理にかかる時間も含まれてしまうので、以下のコードで変数bに代入するのにかかる処理時間を計測する。

public class Test :MonoBehaviour
{
    void Start ()
    {
        check ();
    }

    const int a = 1;

    void check ()
    {
        int b;
        Profiler.BeginSample ("const");

        for (int i = 0; i < 100000; i++) {
            b = 1;
        }
        Profiler.EndSample ();
    }
}

これの計測結果も0.38ms ~ 0.51msと出てしまった。うーん、、、const使用時に実際に何秒かかるかはわからない。

それもそのはずで、const定数は、コンパイルされる時に定数の値と同じ値に置き換えられるため、

        for (int i = 0; i < 100000; i++) {
            b = a; //aはconst定数
        }

上記のコードは以下に置き換えられます。

        for (int i = 0; i < 100000; i++) {
            b = 1;
        }

なので両者の処理時間の違いは計測できないのです。

static readonlyって何?

static readonly = 実行時変数

  • これも定数扱い(読み取り専用)
  • 値型にも参照型にも使える
  • 宣言時に代入しなくてもエラーにはならない
  • 宣言した後は代入できない
  • readonlyだけならコンストラクタ内で初期化可能
  • switch文に使えない
  • constより本当に若干遅い(がきにするほどのレベルじゃない)
エラーは起きない
    static readonly int a = 1;
    static readonly string b = "1";
    static readonly Hoge c = new Hoge (); //参照型もOK
    static readonly int d; //代入しなくてもOK.1が入る
    static readonly Hoge e; //代入しなくてもOK.Nullが入る
宣言した後は代入できない
    void Start ()
    {
        d = 100; //代入できない
    }
    static readonly int d;
readonlyだけならコンストラクタで初期化可能
public class Test2
{
    public Test2 ()
    {
        a = 100;
    }

    readonly int a;
}
エラーが出る(switch文には使えない)
    void AAA ()
    {
        int b = 100;
        switch (b) {
        case a:
            break;
        default:
            break;
        }
    }

    static readonly int a = 100;
20
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
16