C#のconstとreadonlyの違いは何ですか?


1363

違いは何ですか constreadonlyC#では?

いつどちらを使用しますか?


私はこのリンクを見つけるためにいくつかの答えを見下ろす必要がありましたが、それは良いものです。 Eric LippertのC#での不変性
フランクブライス

2
@donstack、実際によると、C#の参照読み取り専用フィールドを割り当てることができ、フィールド宣言とコンストラクタ内で複数回再割り当て。
マルケス

回答:


1289

の明らかな違いは別として

  • constVS readonly値の定義時に値を宣言する必要がある場合、動的に計算できますが、コンストラクターが終了する前に割り当てる必要があります。その後、凍結されます。
  • 'const'は暗黙的にstaticです。ClassName.ConstantNameそれらにアクセスするには表記法を使用します。

微妙な違いがあります。で定義されているクラスを考えAssemblyAます。

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly int I_RO_VALUE;
  public Const_V_Readonly()
  {
     I_RO_VALUE = 3;
  }
}

AssemblyBAssemblyAコードでこれらの値を参照して使用します。これがコンパイルされると、

  • 以下の場合にはconst値が、それは、find-replaceは、値2「に焼き」されるようなものであるAssemblyBのIL。これは、明日I_CONST_VALUE、将来20に更新することを意味します。AssemblyB私がそれを再コンパイルするまで、まだ2があります。
  • 以下の場合にはreadonly値は、似ているrefメモリロケーションへ。値はAssemblyBのILには組み込まれません。これは、メモリの場所が更新された場合、AssemblyB再コンパイルせずに新しい値を取得することを意味します。したがって、I_RO_VALUEが30に更新された場合は、ビルドするだけで済みますAssemblyA。すべてのクライアントを再コンパイルする必要はありません。

したがって、定数の値が変化しないことが確実な場合は、を使用してくださいconst

public const int CM_IN_A_METER = 100;

ただし、変更される可能性のある定数がある場合(たとえば、精度が正確である場合)、または不明な場合は、readonly

public readonly float PI = 3.14;

更新:阿久は彼が最初にこれを指摘した言及を得る必要があります。また、これを学んだ場所に接続する必要があります。効果的なC#-Bill Wagner


77
static-ポイントは、最も重要かつ有用なポイントのようですconsts are implicitly static
LCJ

28
参考値の部分が最も重要です。const値は離れて最適化できます。
CodingBarfield 2013年

22
readonly変数はコンストラクタの外で変更できます(リフレクション)。コンストラクタの外で変数を変更できないようにするのはコンパイラだけです。
Bitterblue 2013年

12
@ mini-me readonly変数は、リフレクションを介しても、コンストラクターが終了すると変更できません。ランタイムはたまたまこれを強制しません。ランタイムはまた、あなたが変更しないことを強制しないように起こるstring.Empty"Hello, world!"が、私はまだこれが行うことを主張していないだろうstring.Empty修正、またはそのコードは、それが想定するべきではありませんstring.Empty常に長さゼロの文字列になります。

7
blogs.msmvps.com/jonskeet/2014/07/16/…は、読み取り専用のオーバーヘッドコストのみで興味深い読み取りです
CADブローク

275

constsには落とし穴があります!別のアセンブリから定数を参照すると、その値は呼び出し元のアセンブリに直接コンパイルされます。このようにして、参照されているアセンブリの定数を更新しても、呼び出し元のアセンブリでは変更されません。


8
逆コンパイル(Reflector、ILSpy、..)では、定数は決して同じアセンブリまたは別のアセンブリに関係なく誰からも参照されないため、コンパイルされたコードでの定数の使用をまったく分析できません。
springy76 2016

159

定数

  • 定数はデフォルトで静的です
  • これらは、コンパイル時に値を持っている必要があります(たとえば、3.14 * 2にすることができますが、メソッドを呼び出すことはできません)。
  • 関数内で宣言できます
  • それらを使用するすべてのアセンブリにコピーされます(すべてのアセンブリは値のローカルコピーを取得します)
  • 属性で使用できます

読み取り専用インスタンスフィールド

  • コンストラクタが終了するまでに、値を設定しておく必要があります
  • インスタンスの作成時に評価されます

静的な読み取り専用フィールド

  • コード実行がクラス参照にヒットしたときに評価されます(新しいインスタンスが作成されたとき、または静的メソッドが実行されたとき)
  • 静的コンストラクタが完了するまでに評価された値が必要です
  • これらにThreadStaticAttributeを設定することはお勧めしません(静的コンストラクターは1つのスレッドでのみ実行され、そのスレッドの値を設定します。他のすべてのスレッドはこの値を初期化しません)

58

追加するだけで、参照タイプのReadOnlyは、参照を値のみではなく読み取り専用にします。例えば:

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly char[] I_RO_VALUE = new Char[]{'a', 'b', 'c'};

  public UpdateReadonly()
  {
     I_RO_VALUE[0] = 'V'; //perfectly legal and will update the value
     I_RO_VALUE = new char[]{'V'}; //will cause compiler error
  }
}

string定数として使用できるもの以外に参照タイプはありますか?
springy76 2016

const文字列以外の参照型を使用できますが、定数は値のみを持つことができますnull
Mike Rosoft、2018

40

これはそれを説明します。概要:constは宣言時に初期化する必要があります。readonlyはコンストラクターで初期化できます(したがって、使用するコンストラクターによって値が異なります)。

編集:微妙な違いについては上記の紀州の落とし穴を参照してください


32

const:どこでも変更できません。

readonly:この値は、コンストラクターでのみ変更できます。通常の機能では変更できません。


26

読み取り専用の小さな落とし穴があります。読み取り専用フィールドは、コンストラクター内で複数回設定できます。値が2つの異なるチェーンコンストラクターに設定されている場合でも、値は許可されます。


public class Sample {
    private readonly string ro;

    public Sample() {
        ro = "set";
    }

    public Sample(string value) : this() {
        ro = value; // this works even though it was set in the no-arg ctor
    }
}

26

定数メンバーはコンパイル時に定義され、実行時に変更することはできません。定数はconstキーワードを使用してフィールドとして宣言され、宣言されたとおりに初期化する必要があります。

public class MyClass
{
    public const double PI1 = 3.14159;
}

readonly部材は、不変の値を表すことで一定のようです。違いは、readonlyメンバーは実行時にコンストラクターで初期化できることと、宣言されたとおりに初期化できることです。

public class MyClass1
{
     public readonly double PI2 = 3.14159;

     //or

     public readonly double PI3;

     public MyClass2()
     {
         PI3 = 3.14159;
     }
}

const

  • として宣言することはできませんstatic(暗黙的に静的です)。
  • 定数の値はコンパイル時に評価されます
  • 定数は宣言時にのみ初期化されます

読み取り専用

  • インスタンスレベルまたは静的のいずれかです。
  • 値は実行時に評価されます
  • readonlyは宣言またはコンストラクターのコードで初期化できます

6
静的であってはならず、静的です。宣言できないことstatic const int i = 0;
nawfal

constメソッド内で宣言ができない理由を説明できますか?
Minh Tran

21

constはコンパイル時の定数ですが、readonlyを使用すると、実行時に値を計算して、コンストラクターまたはフィールド初期化子で設定できます。したがって、「const」は常に定数ですが、「readonly」は割り当てられると読み取り専用になります。

C#チームのEric Lippertは、さまざまなタイプの不変性に関する詳細情報を持っています


15

constがバージョンセーフでない、または参照型に関連していないことを示す別のリンクを次に示します。

まとめ

  • constプロパティの値はコンパイル時に設定され、実行時に変更できません
  • constを静的としてマークすることはできません。キーワードは、読み取り専用フィールドとは異なり、静的であることを示します。
  • constは、値(プリミティブ)型以外にはできません
  • readonlyキーワードは、フィールドを変更不可としてマークします。ただし、プロパティはクラスのコンストラクター内で変更できます
  • readonly onlyキーワードをstaticと組み合わせて、const(少なくとも表面上)と同じように動作させることもできます。2つの間のILを見ると、顕著な違いがあります。
  • ILではconstフィールドが「リテラル」としてマークされ、読み取り専用は「initonly」です

11

読み取り専用:実行時にCtorを介して値を変更できます。しかし、メンバー関数を介してではありません

定数:デフォルトの静的。どこからでも値を変更することはできません(Ctor、関数、ランタイムなど、どこにも変更できません)


これら2つのテイクアウトのためだけに4つのパラグラフを読まないようにしてくれてありがとう...
Don Cheadle


6

const値はすべてのオブジェクトで同じであり、リテラル式で初期化する必要があると思いますが、readonlyインスタンス化ごとに異なる可能性があります...


5

私たちのオフィスのチームメンバーの1人が、const、static、およびreadonlyをいつ使用するかについて次のガイダンスを提供しました。

  • クラスのすべてのインスタンスまたはコンシューマーが値を変更してはならない場所にアクセスできるようにする必要があることを実行時に確認できる型の変数(文字列リテラル、int、double、enums、...)がある場合は、constを使用します。
  • クラスのすべてのインスタンスまたはコンシューマーが値を変更できる場所にアクセスできるようにするデータがある場合は、staticを使用します
  • クラスのすべてのインスタンスまたはコンシューマーが値を変更してはならない場所にアクセスできるようにする必要がある実行時(オブジェクト)のタイプの変数がある場合は、静的なreadonlyを使用します
  • オブジェクト作成時に変更すべきではないインスタンスレベルの変数がある場合は、readonlyを使用します

最後に、constフィールドは静的ですが、その逆は当てはまりません。


1
あなたは「会話」を意味すると思います。その逆は、「非constフィールドは静的ではない」です。それは本当かもしれないし、そうでないかもしれない。逆に、「静的フィールドは(常に)constです」は当てはまりません。
マイケルブラックバーン

5

どちらも定数ですが、コンパイル時にconstも使用できます。これは、違いの1つの側面は、属性コンストラクターへの入力としてconst変数を使用できることですが、読み取り専用変数は使用できないことです。

例:

public static class Text {
  public const string ConstDescription = "This can be used.";
  public readonly static string ReadonlyDescription = "Cannot be used.";
}

public class Foo 
{
  [Description(Text.ConstDescription)]
  public int BarThatBuilds {
    { get; set; }
  }

  [Description(Text.ReadOnlyDescription)]
  public int BarThatDoesNotBuild {
    { get; set; }
  }
}

5
  • いつ使用するconstreadonly

    • const

      • コンパイル時の定数:絶対定数、宣言時に値が設定され、ILコード自体にあります
    • readonly

      • ランタイム定数:構成ファイルを介してコンストラクター/初期化で設定できますApp.config(ただし、一度初期化すると変更できません)

4

constとマークされた変数は、厳密に型指定された#defineマクロに過ぎず、コンパイル時にconst変数参照はインラインリテラル値に置き換えられます。結果として、この方法で使用できるのは、特定の組み込みプリミティブ値タイプのみです。readonlyとマークされた変数は、コンストラクターで実行時に設定でき、それらの読み取り専用性は実行時にも強制されます。これに関連するいくつかの小さなパフォーマンスコストがありますが、これは、任意のタイプ(参照タイプも含む)で読み取り専用を使用できることを意味します。

また、const変数は本質的に静的ですが、読み取り専用変数は必要に応じてインスタンス固有にすることができます。


constが強く型付けされた #defineマクロであることを追加しました。そうしないと、すべてのCまたはC ++の人々を怖がらせる可能性があります。:-)
Jason Baker、

4

CONST

  1. constキーワードはフィールドまたはローカル変数に適用できます
  2. 宣言時にconstフィールドを割り当てなければなりません
  3. const値はコンパイル後にILコード自体に埋め込まれるため、メモリは割り当てられません。これは、const変数のすべての出現箇所を検索して、その値で置き換えるのと似ています。したがって、コンパイル後のILコードには、const変数の代わりにハードコードされた値が含まれます。
  4. C#のConstはデフォルトで静的です。
  5. 値はすべてのオブジェクトで一定です
  6. DLLのバージョン管理の問題があります-これは、パブリックのconst変数またはプロパティを変更するたびに(実際には、理論的に変更されることは想定されていません)、この変数を使用する他のdllまたはアセンブリを再構築する必要があることを意味します
  7. C#の組み込み型のみを定数として宣言できます
  8. 定数フィールドを参照または出力パラメーターとして渡すことはできません

読み取り専用

  1. readonlyキーワードは、ローカル変数ではなくフィールドにのみ適用されます
  2. 宣言時またはコンストラクターで読み取り専用フィールドを割り当てることができます。他のメソッドでは割り当てることができません。
  3. 読み取り専用フィールドに動的メモリが割り当てられ、実行時に値を取得できます。
  4. Readonlyは、クラスのインスタンスのみを介してアクセスされるように作成されたオブジェクトに属します。クラスメンバーにするには、readonlyの前にstaticキーワードを追加する必要があります。
  5. 値は、使用されるコンストラクターに応じて異なる場合があります(クラスのオブジェクトに属しているため)
  6. 非プリミティブ型(参照型)を読み取り専用として宣言した場合、参照のみが不変であり、それに含まれるオブジェクトは変更できません。
  7. 値は実行時に取得されるため、読み取り専用フィールド/プロパティにdllバージョン管理の問題はありません。
  8. コンストラクタコンテキストでは、読み取り専用フィールドをrefまたはoutパラメータとして渡すことができます。

3

別の落とし穴

constは実際には基本的なデータ型でのみ機能するため、クラスを操作したい場合は、ReadOnlyを使用するよう「強制」されていると感じるかもしれません。ただし、トラップには注意してください。ReadOnlyは、オブジェクトを別のオブジェクトに置き換えることができないことを意味します(別のオブジェクトを参照させることはできません)。しかし、オブジェクトへの参照を持つすべてのプロセスは、内部の値を自由に変更できますのます。

したがって、ReadOnlyはユーザーが変更できないことを意味するものと誤解しないでください。C#には、クラスのインスタンス化によって内部値が変更されるのを防ぐ単純な構文はありません(私の知る限り)。


はい、それはより一般的なテーマです。arraylistを公開するget onlyプロパティがある場合でも、arraylistを変更できます。そのプロパティに別のarraylistを設定することはできませんが、ユーザーがarraylistを変更するのを止めることはできません。
岐阜

3

Aは、constである必要があり、ハードコードされたところのように、readonlyすることができるコンストラクタで設定されたクラスの。


3

C#.Netのconstフィールドとreadonlyフィールドには顕著な違いがあります

constはデフォルトでは静的であり、後で変更できない定数値で初期化する必要があります。コンストラクタでも値の変更は許可されていません。すべてのデータ型で使用できるわけではありません。exDateTimeの場合。DateTimeデータ型では使用できません。

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public readonly string Name = string.Empty; //No error, legal

readonlyは静的として宣言できますが、必須ではありません。宣言時に初期化する必要はありません。その値は、コンストラクターを使用して割り当てまたは変更できます。そのため、インスタンスクラスメンバーとして使用すると利点があります。2つの異なるインスタンス化では、読み取り専用フィールドの値が異なる場合があります。元の場合-

class A
{
    public readonly int Id;

    public A(int i)
    {
        Id = i;
    }
}

次に、次のように、読み取り専用フィールドをインスタント固有の値で初期化できます。

A objOne = new A(5);
A objTwo = new A(10);

ここでは、インスタンスobjOneのreadonlyフィールドの値は5で、objTwoの値は10です。これは、constを使用すると不可能です。


2

定数はリテラル値としてコンシューマーにコンパイルされますが、静的文字列は定義された値への参照として機能します。

演習として、外部ライブラリを作成してコンソールアプリケーションで使用し、ライブラリの値を変更して再コンパイルし(コンシューマプログラムを再コンパイルせずに)、DLLをディレクトリにドロップしてEXEを手動で実行してください。定数文字列は変更されません。


私はそれが真実であることを心から疑っています...私はチェックに行きます。
ljs 2009

-これはあなたのC#を改善するための50個の特定の方法の一つであるamazon.co.uk/Effective-Specific-Ways-Improve-Your/dp/0321245660/...
ラス・カム


@Andrew Hare-はい、チェックしました。私は非常に驚いています、それは本当の落とし穴です、私はそれで本当に非常に驚いています。
ljs 2009

ただし、ここではポインターという単語の使用に反対しています。これはポインターではなく、参照であり、アンセーフモードでアンマネージポインターを操作できるため、C#に違いがあります。この2つを区別することが重要です。
ljs 2009

2

絶え間ない

constフィールドが定義されている場合、値を提供する必要があります。次に、コンパイラは定数の値をアセンブリのメタデータに保存します。つまり、定数はboolean、char、byteなどのプリミティブ型に対してのみ定義できます。定数は常にインスタンスメンバーではなく、静的メンバーと見なされます。

読み取り専用

読み取り専用フィールドは、実行時にのみ解決できます。つまり、フィールドが宣言されている型のコンストラクターを使用して、値の値を定義できます。検証は、読み取り専用フィールドがコンストラクター以外のメソッドによって書き込まれないことをコンパイラーが行います。

この記事で説明されている両方の詳細


1

主に; 実行時に静的な読み取り専用フィールドに値を非定数値に割り当てることができますが、constには定数値を割り当てる必要があります。


1

constとreadonlyは似ていますが、完全に同じではありません。constフィールドはコンパイル時の定数です。つまり、その値はコンパイル時に計算できます。読み取り専用フィールドを使用すると、型の構築中に一部のコードを実行する必要がある追加のシナリオが可能になります。作成後、読み取り専用フィールドは変更できません。

たとえば、constメンバーを使用して、次のようなメンバーを定義できます。

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

3.14や0などの値はコンパイル時の定数だからです。ただし、タイプを定義し、そのプレハブインスタンスをいくつか提供する場合を考えてみます。たとえば、Colorクラスを定義して、Black、Whiteなどの一般的な色に「定数」を提供したい場合があります。constメンバーを使用してこれを行うことはできません。右側がコンパイル時の定数ではないためです。通常の静的メンバーでこれを行うことができます:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

しかし、おそらく黒と白の値を交換することによって、Colorのクライアントがそれをいじるのを防ぐ方法はありません。言うまでもなく、これはColorクラスの他のクライアントを驚かせます。「読み取り専用」機能は、このシナリオに対処します。宣言にreadonlyキーワードを導入するだけで、クライアントコードが不正に動作するのを防ぎながら、柔軟な初期化を維持できます。

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

constメンバーは常に静的ですが、読み取り専用メンバーは通常のフィールドと同様に静的でもそうでなくてもかまいません。

これら2つの目的で単一のキーワードを使用することは可能ですが、これによりバージョン管理の問題またはパフォーマンスの問題が発生します。この(const)に単一のキーワードを使用して、開発者が書いたと仮定します。

public class A
{
    public static const C = 0;
}

別の開発者がAに依存するコードを記述しました。

public class B
{
    static void Main() {
        Console.WriteLine(A.C);
    }
}

さて、生成されたコードは、ACがコンパイル時の定数であるという事実に依存できますか?つまり、ACの使用を値0に置き換えることができますか?これに「はい」と答えると、それはAの開発者がACの初期化方法を変更できないことを意味します。これは、許可なくAの開発者の手を結びます。この質問に「いいえ」と答えると、重要な最適化が行われなくなります。おそらく、Aの作者は、ACが常にゼロであることを肯定しています。constとreadonlyの両方を使用すると、Aの開発者が意図を指定できます。これにより、バージョン管理の動作とパフォーマンスが向上します。


1

ReadOnly:値は、クラスのコンストラクターから一度だけ初期化されます。
const:どの関数でも初期化できますが、1回だけです


1

違いは、静的な読み取り専用フィールドの値は実行時に設定されるため、プログラムの実行ごとに異なる値を持つ可能性があることです。ただし、constフィールドの値はコンパイル時定数に設定されます。

要確認:参照タイプの場合、両方の場合(静的およびインスタンス)、readonly修飾子は、フィールドへの新しい参照の割り当てを防止するだけです。具体的には、参照によってポイントされるオブジェクトを不変にしません。

詳細については、このトピックに関するC#のよく寄せられる質問を参照してください:http : //blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx


1

定数変数は、コンパイル時に宣言および初期化されます。ワード後の値は変更できません。読み取り専用変数は、クラスのStaticコンストラクターからのみ初期化されます。読み取り専用は、実行時に値を割り当てる場合にのみ使用されます。


1

定数:アプリケーションのライフタイム中の絶対定数値。

読み取り専用:実行時に変更できます。


1
変更できる「読み取り専用」の定義に誤りがあります。「変更」とは、「実行時に設定できる」のように、「設定」を意味したと思います。
アーメド

0

人々が上で言ったことに追加するための一つ。読み取り専用の値を含むアセンブリがある場合(たとえば、readonly MaxFooCount = 4;)、そのアセンブリの新しいバージョンを別の値で出荷することで(たとえば、readonly MaxFooCount = 5;)、呼び出し側のアセンブリが表示する値を変更できます。

しかし、constを使用すると、呼び出し元がコンパイルされたときに呼び出し元のコードに組み込まれます。

このレベルのC#の熟練度に達している場合は、Bill Wagnerの著書「Effective C#:50 Specific Ways to Improve C#」でこの質問に詳細に答える(そしてその他49のこと)の準備ができています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.