静的な読み取り専用とconst


1387

私は周りに読んだconststatic readonlyフィールド。定数値のみを含むクラスがいくつかあります。私たちのシステム内のさまざまなものに使用されます。だから私は私の観察が正しいかどうか疑問に思っています:

これらの種類の定数値は、常にstatic readonly公開されているすべてのものに適用する必要がありますか?そしてconst、内部/保護/プライベート値にのみ使用しますか?

何がお勧めですか?static readonlyフィールドを使用するのではなく、プロパティを使用する必要がありますか?


5
ここで私はちょうどの賛成で見つかった非常に興味深い一つのケースですstatic readonly内部でのconstを使用してみてくださいIEnumerator引き金と思われるunrecheableを yield、あなたは恐ろしい「内部コンパイラエラー」を取得します。Unity3D外でコードをテストしませんでしたが、これはモノまたは.NET バグのいずれかであると確信しています。それは、C#のそれにもかかわらず問題。
cregox 2013


8
別の違いは、スイッチではconst文字列を使用できますが、静的な読み取り専用文字列は使用できないことです
flagg19

7
static readonlyswitch-caseステートメントではcase変数として使用できませんconst。このために必要です。
Mostafiz Rahman、2015

3
static readonly属性パラメーターとしても使用できない
Dread Boy

回答:


940

public static readonlyフィールドは少し変わっています。public staticプロパティ(のみget)はより一般的です(おそらくprivate static readonlyフィールドによってサポートされます)。

const値は呼び出しサイトに直接書き込まれます。これは両刃です:

  • 実行時に、おそらくconfigから値がフェッチされた場合、それは役に立たない
  • constの値を変更した場合、すべてのクライアントを再構築する必要があります
  • しかし、メソッド呼び出しを回避するため、より高速になる可能性があります...
  • ...とにかくJITによってインライン化された可能性があります

値が決して変わらない場合は、constは問題ありませんZero -etcは妥当なconstsを作成します; pそれ以外は、staticプロパティがより一般的です。


13
なぜフィールドのプロパティなのか?不変クラスの場合、違いはありません。
マイケル・ヘドペス2009

73
@Michael-いつもと同じ理由。それは実装を隠します。(後で)遅延読み込み、構成ベース、ファサードなどが必要になる場合があります。実際には、どちらでも大丈夫です...
マークグラベル

42
@CoffeeAddictの定義によると、定数は構成ファイルから値を引き出していません。コンパイル時にリテラルとして焼き付けられます。実行時に定数使用できる唯一の方法は、フィールドのリフレクションを使用することです。それ以外の場合、コンパイラー定数の使用法をリテラルの使用法に既に置き換えています。つまり、コード内のメソッドが6つの定数を使用していて、それをILとして検査する場合、定数のルックアップについての言及はありません。リテラル値は単にその場で読み込まれます
マークグラベル

37
@MarcGravell-注意:readonlyフィールドはswitch / caseステートメントでは使用できませんが、代わりににする必要がありますconst
ルチアーノ

7
@didibusフィールドをプロパティに変更すると、実際にはAPIが壊れます。C#のフィールドは変数のように機能しますが、C#のプロパティはゲッターメソッドやセッターメソッドを記述するための構文ヘルパーです。この違いは、他のアセンブリが関係する場合に重要です。フィールドをプロパティに変更し、他のアセンブリがこのフィールドに依存している場合、それらの他のアセンブリを再コンパイルする必要があります。
Stephen Booher 2013

237

コンシューマーが別のアセンブリーにあるstatic readonly場合に使用します。持つと消費者二つの異なるアセンブリでは良い方法である足で自分を撃ちますconst


5
そのため、一部の人が言及または示唆したように、実際に既知の定数である値に対してのみconstを使用することは、それらが公開されている場合、内部、保護、またはプライベートアクセススコープ用に予約する必要があると考えます。
jpierson 2011年

1
@Dioそれがまだ存在する理由は、それ自体が問題ではないためです-これは注意する必要がありますが、アセンブリの境界を越えてconstをインライン化する機能は、パフォーマンスにとって良いことです。「一定」とは「決して変わらない」ということを理解するだけのことです。
Michael Stum

1
@MichaelStumわかりました「問題」と呼んではいけません。私の作業では、constを使用してアセンブリ間で共有していますが、展開またはコードの配布ごとに再コンパイルします。それにもかかわらず、この事実は間違いなくそれに注意する価値があります。
Dio Phung、2014

1
したがって、一般的に、internal constまたはpublic static readonly必要な可視性によって異なります。
Iiridayn

2
@Iiridaynええ、それはそれを見るのに悪い方法ではありません。考慮すべきエッジケースがいくつかあり(たとえば、Reflectionを使用する場合、または属性に値が必要な場合)、有効な用途がありますpublic const(たとえば、標準の任意の部分です。XMLで作業するときはいつでも、の束を含む名前空間ファイルpublic const string。)しかし、一般的にpublic constは、影響を適切に検討した後でのみ使用する必要があります。
Michael Stum

200

注目すべきいくつかのより関連するもの:

const int a

  • 初期化する必要があります。
  • 初期化はコンパイル時に行う必要があります。

読み取り専用整数

  • 初期化せずにデフォルト値を使用できます。
  • 初期化は実行時に行うことができます(編集:コンストラクター内のみ)。

39
ctorだけ。
Amit Kumar Ghosh

1
コンストラクター内だけでなく、宣言内にもあります(docs.microsoft.com/en-us/dotnet/csharp/language-reference/…)。
deChristo

176

これは、他の回答の補足です。私はそれらを繰り返しません(今から4年後)。

a constと非constのセマンティクスが異なる場合があります。例えば:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

を出力しますがTrue

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

書き込みますFalse

その理由は、メソッドx.Equalsに2つのオーバーロードがあることです。1つはshortSystem.Int16)を受け取り、もう1つはobjectSystem.Object)を受け取ります。ここで問題は、どちらか一方が私のy主張に当てはまるかどうかです。

場合y、コンパイル時定数(リテラル)であるconst場合、それは暗黙の変換が存在しないことが重要になるから int shortものとするint定数であり、その値の範囲内にあることC#コンパイラを検証することを条件とするshort(しこれ42です)。C#言語仕様の暗黙的な定数式変換を参照してください。したがって、両方のオーバーロードを考慮する必要があります。オーバーロードは、Equals(short)(任意のが好適でshortあるobjectが、すべてではないobjectですshort)。だから、yに変換されshort、その過負荷が使用されています。次に、同じ値のEquals2つshortを比較すると、が得られtrueます。

場合y一定ではなく、何の暗黙の変換intにはshort存在しません。これは、一般にintが大きすぎてに収まらない場合があるためshortです。(明示的な変換は存在しますが、私は言いませんでしたのでEquals((short)y)、それは関係ありません。)1つのオーバーロードのみが適用されることがわかりEquals(object)ます。なのでy箱入りobjectです。次にEquals、a System.Int16をa と比較System.Int32します。実行時の型が一致しないため、結果はになりfalseます。

一部の(まれな)ケースでは、const型メンバーをstatic readonlyフィールドに(または可能な場合はその逆に)変更すると、プログラムの動作が変更される可能性があると結論付けます。


17
受け入れられた回答への良い追加。データ型の適切な変換とその他の同様のガイドライン(トライキャッチなど)は経験豊富なプログラマーの主食であり、コンパイラーに任せるべきではないことを付け加えたいと思います。それにもかかわらず、私はここから何か新しいことを学びました。ありがとうございました。
2013年

うわー、私は長い間C#でプログラミングしてきましたが、shortの範囲内のconst intが暗黙的にshortに変換されるとは思いもしませんでした。それは奇妙なことだと言わざるを得ません。私はC#が大好きですが、あまり価値を加えていないように見えるが、常に検討するために必要な多くの頭脳力を追加するこれらの奇妙な矛盾は、特に初心者にとっては迷惑です。
Mike Marynowski

@MikeMarynowski確かにそうだ。しかし、彼らはその声明をshort x = 42;合法にするために(他の理由の中でも)そのルールを作ったと思います。そこにはint、つまりリテラルがあり42、暗黙的にに変換されますshort x。しかし、その後、これを数値リテラルだけに制限した可能性があります。しかし、彼らはshort x = y;where yがとして定義されているようなものも許可することを選択しconst int y = 42;、それから彼らはこれで終わりました。
Jeppe Stig Nielsen

88

注意すべき点の1つは、constがプリミティブ/値型に制限されていることです(例外は文字列です)。


30
実際にconstは他の型にも使用できますが、nullに初期化する必要があるため、使用できなくなります:)
nawfal

6
のような例外System.Exception?:)
Memet Olsen 2014

4
@nawfalより正確には、唯一の値型のためにconst使用することができる、ですsbytebyteshortushortintuintlongulongcharfloatdoubledecimalbool、および任意のenum種類。またはまたはconstなどの他の値タイプには使用できません。また、構造体には使用できません(「プリミティブ」型と見なされる場合があります。プリミティブ型という用語はC#では混乱します)。↵↵は、すべての参照タイプに使用できます。タイプがの場合、任意の文字列値を指定できます。それ以外の場合、値はでなければなりません。DateTimeTimeSpanBigIntegerIntPtrconststringnull
Jeppe Stig Nielsen

@JeppeStigNielsen-私は最近これについてサービスとの議論をました -彼はあなたが何か(値と参照型)constを使用して作ることができると指摘しましたdefault。以下のためstructのタイプ、それがデフォルト値に設定されているすべてのメンバーとインスタンスです。
Wai Ha Lee

28

静的読み取り専用static実行時にコンストラクターを介して値を変更できます。しかし、メンバー関数を介してではありません。

定数:デフォルトstatic。どこからでも値を変更することはできません(Ctor、Function、runtimeなど)。

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

あなたは私のリポジトリを見ることができます:C#プロパティタイプ


1
悪いニュース...リンク切れ!
Fer R


グッドスニペットサイアムভাই:)
Muhammad Ashikuzzaman '05

25

readonlyキーワードが異なっているconstキーワード。constフィールドは、フィールドの宣言時に初期化することができます。readonlyフィールドは、宣言で、またはコンストラクタのいずれかで初期化することができます。したがって、readonlyフィールドは、使用されるコンストラクターに応じて異なる値を持つことができます。また、constフィールドはコンパイル時の定数ですが、readonly実行時定数に使用できます。

ここで、短くて明確なMSDNリファレンス


16

constreadonly似ていますが、まったく同じではありません。

constフィールドには、その値は、コンパイル時に計算できることを意味し、コンパイル時定数です。あreadonlyフィールドには、いくつかのコードは、タイプの建設中に実行されなければならない追加のシナリオが可能になります。作成後、readonlyフィールドは変更できません。

たとえば、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, green, blue) = (r, g, 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, green, blue) = (r, g, 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の開発者が意図を指定できます。これにより、バージョン管理の動作とパフォーマンスが向上します。


12

私の好みはconstを使用することです限りいつでもことです。これは、前述のとおり、リテラル式または評価を必要としないものに制限されています。

この制限にぶつかった場合は、静的な読み取り専用にフォールバックしますが、注意点が1つあります。Marcがここで言及しているように、私は一般に、getterおよびバッキングプライベート静的読み取り専用フィールドでパブリック静的プロパティを使用します


7

Const: Constは「定数」にすぎません。値は定数ですが、コンパイル時の変数です。そして、それに値を割り当てることは必須です。デフォルトでは、constは静的であり、プログラム全体でconst変数の値を変更することはできません。

静的読み取り専用:静的読み取り専用タイプの変数の値は、実行時に割り当てるか、コンパイル時に割り当てて、実行時に変更できます。ただし、この変数の値は静的コンストラクターでのみ変更できます。さらに変更することはできません。実行時に変更できるのは1回だけです

リファレンス:c-sharpcorner


6

静的な読み取り専用フィールドは、新しいバージョンで変更される可能性のある値を他のアセンブリに公開する場合に有利です。

たとえば、アセンブリXが次のように定数を公開するとします。

public const decimal ProgramVersion = 2.3;

アセンブリがこの定数をY参照Xして使用している場合、値2.3はYコンパイル時にアセンブリにベイク処理されます。これは、X後で2.4に設定された定数で再コンパイルされた場合、が再コンパイルされるYまで、古い値2.3を引き続き使用することを意味しYます。静的な読み取り専用フィールドは、この問題を回避します。

これを別の見方で見ると、将来変化する可能性のある値はすべて定義上一定ではないため、1として表すべきではありません。


3

const:

  1. 値は宣言時に与える必要があります
  2. コンパイル時定数

読み取り専用:

  1. 値は、コンストラクターを使用して宣言時または実行時に指定できます。値は、使用するコンストラクターによって異なる場合があります。
  2. 実行時定数

3

Const:const変数の値は宣言とともに定義する必要があり、その後は変更されません。constは暗黙的に静的であるため、クラスインスタンスを作成しなくてもアクセスできます。これはコンパイル時に値を持っています

ReadOnly:実行時に宣言およびコンストラクタを使用しながら定義できるreadonly変数値。読み取り専用変数は、クラスインスタンスなしではアクセスできません。

静的読み取り専用:宣言時に定義できる静的readonly変数の値と、静的コンストラクタを通じてのみ定義でき、他のコンストラクタでは定義できません。これらの変数は、クラスインスタンスを作成せずにアクセスできます(静的変数として)

異なるアセンブリで変数を使用する必要がある場合は、静的なreadonlyの方が適しています。以下のリンクで詳細を確認してください

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/


なぜ回答に反対票を投じたのか教えていただけますか?私もここで更新できます。
user1756922 2018

DVではありませんが、この回答は、ここでのすでに包括的な回答に実際には何も追加しない可能性があります。
マーク

確かに、我々はこのプロジェクトに(お互いを参照)相互運用クラスファイルを別のjarファイルを生成する複数の人が持っていたし、彼らは周りにコピーされていたので、公共のconst stringは、トラブルをバージョン管理していた後半90年代のJavaバックに覚えている
ジョージBirbilis

2

C#.Netのconstとstatic readonlyフィールドの間には小さな違いがあります

constは、コンパイル時に値で初期化する必要があります。

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

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

readonlyは静的として宣言できますが、必須ではありません。宣言時に初期化する必要はありません。その値は、コンストラクターを使用して一度割り当てたり変更したりできます。そのため、読み取り専用フィールドの値を一度変更する可能性があります(静的かどうかは関係ありません)。これは、constでは不可能です。


0

定数は、名前が示すように、変更されないフィールドであり、通常、コードのコンパイル時に静的に定義されます。

読み取り専用変数は、特定の条件下で変更される可能性があるフィールドです。

最初に定数のように宣言するときに初期化できますが、通常、コンストラクター内でのオブジェクトの構築中に初期化されます。

上記の条件では、初期化後に変更することはできません。

静的な読み取り専用は、静的で変更されない場合は、私にとっては不適切な選択のように聞こえるので、パブリックconstを使用します。変更できる場合は、定数ではなく、必要に応じて、読み取りを使用できます。 -onlyまたは単なる通常の変数。

また、もう1つの重要な違いは、定数はクラスに属し、読み取り専用変数はインスタンスに属していることです。


0

const(コンパイル時に決定される)は、switchステートメントや属性コンストラクターなどのように、読み取り専用staticができない場合に使用できます。これは、読み取り専用フィールドは実行時にのみ解決され、一部のコード構造にはコンパイル時間の保証が必要なためです。読み取り専用staticは、コンストラクターで計算できます。これは、多くの場合、不可欠で有用なものです。違いは機能的であり、私の意見ではそれらの使用法があるはずです。

メモリ割り当ての観点から、少なくとも文字列(参照型であるため)では、両方がインターンされ、1つのインターンされたインスタンスを参照するという点で違いはないようです。

個人的には、特にコンパイル時にほとんどの値が必要とされないため、より意味論的で論理的に理解できるため、私のデフォルトは読み取り専用の静的です。そして、ちなみに、マークされた回答が示すように、パブリックな読み取り専用の統計は珍しくも珍しくもありませんSystem.String.Empty


0

conststatic readonlyの宣言のもう1つの違いは、メモリ割り当てです。

静的フィールドは、そのタイプのインスタンスではなく、オブジェクトのタイプに属しています。その結果、クラスが初めて参照されると、静的フィールドは残りの時間メモリ内で「存続」し、静的フィールドの同じインスタンスが型のすべてのインスタンスによって参照されます。

一方、constフィールドは「型のインスタンスに属します。

割り当て解除のメモリの方が重要な場合は、constを使用することをお勧めします。速度の場合は、静的なreadonlyを使用します

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