String.Emptyが定数でないのはなぜですか?


188

.NetでString.Emptyが定数ではなく読み取り専用になるのはなぜですか?誰かがその決定の背後にある理由が何であるかを知っているのかと思っています。


5
この質問はこれを解決できます。簡単な答えは、誰にもわかりません...
gdoronはモニカをサポートし

ええ、Eric Lippertの回答に対して+1をありがとう。
トラビス

特にDecimal.Zeroがconst(ユーザーの観点から)であることを考えると
Hamish Grubijan

回答:


147

static readonly代わりに使用される理由constは、Microsoftが共有ソース共通言語インフラストラクチャ2.0リリースでここに示したように、アンマネージコードで使用するためです。確認するファイルはsscli20\clr\src\bcl\system\string.csです。

空の定数は、空の文字列値を保持します。コンパイラがこれをリテラルとしてマークしないように、Stringコンストラクタを呼び出す必要があります。

これをリテラルとしてマークすると、ネイティブからアクセスできるフィールドとして表示されなくなります。

CodeProjectのこの便利な記事からこの情報を見つけました。


(ジョンスキートが...できなかったため)ここを参照してくださいあなたはこのコメントを説明できる場合、私は本当に感謝:stackoverflow.com/questions/8462697/...
gdoronがモニカ支援している

2
@gdoron:私の推測(そしてそれは推測です)はこれです。値がリテラル(定数)として定義されている場合、その値は参照される場所に挿入されますが、リテラルとして定義されていない場合、値のソースが参照され、実際の値が実行時に取得されます。後者は実行時に文字列の適切なマーシャリングがネイティブと.NETの間で発生することを保証しているのではないかと思います-リテラルの場合、おそらくネイティブコンパイラはリテラル値を何らかの方法でネイティブコードに取り込む必要があります。実現可能。しかし、これはすべて私の推測です。
ジェフイェーツ

7
メソッドのデフォルトのパラメータ値にはstring.Emptyではなく、 ""を使用する必要があることを意味します。これは少し迷惑です。
nicodemus13 2012年

17
「String.Emptyをは意図的な意思を示している」、間違いのように見えることができます
クリストファー・スティーブンソン

3
@JeffYates一貫していないという事実はすでに迷惑であると付け加えておきます。人々は残りのコードを見て、「なぜString.Emptyの代わりにここで「」を使用しているのか」と疑問に思うでしょう。String.Emptyその理由だけでもう使わないことを真剣に考えています。
julealgon

24

ここには多くの混乱と悪い反応があると思います。

まず、constフィールドはstaticメンバーです(インスタンスメンバーではありません)。

C#言語仕様のセクション10.4定数を確認してください。

定数は静的メンバーと見なされますが、定数宣言は静的修飾子を必要とせず、許可もしません。

public constメンバーが静的である場合、定数が新しいオブジェクトを作成するとは考えられません。

このため、次のコード行は、新しいオブジェクトの作成に関してまったく同じことを行います。

public static readonly string Empty = "";
public const string Empty = "";

以下は、2つの違いを説明するMicrosoftからのメモです。

readonlyキーワードは、constキーワードとは異なります。constフィールドは、フィールドの宣言時にのみ初期化できます。読み取り専用フィールドは、宣言時またはコンストラクターで初期化できます。したがって、読み取り専用フィールドは、使用するコンストラクターに応じて異なる値を持つことができます。また、constフィールドはコンパイル時の定数ですが、readonlyフィールドはランタイム定数に使用できます...

だから私はここでもっともらしい答えはジェフ・イェーツのものだと思う。


親切な単語の+1と、constおよびstatic readonlyのC#仕様に関する説明。
ジェフイェーツ

17
この再読んで、私がいることを同意const stringしてstatic readonly string同じことを行います。静的な読み取り専用の値が参照されるのに対し、リンクされたコードではconst値が置き換えられます。constライブラリーBで使用されるライブラリーA がある場合、ライブラリーBはそのconst変数へのすべての参照をリテラル値で置き換えます。その変数がstatic readonly代わりにあった場合、それは参照され、その値は実行時に決定されます。
ジェフイェーツ

3
ライブラリを参照するとき、ジェフのポイントは重要です。Bを再コンパイルせずに Aを再コンパイルして再配布すると、Bは引き続き古い値を使用します。
Mark Sowul 2013

4
String.Empty read only instead of a constant?

文字列を定数にすると、コンパイラは実際の文字列に置き換えます。コード全体に同じ文字列を入力します。コードを実行すると、別のメモリからその文字列を何度も読み取る必要があります。データ。

文字列を1つの場所でのみ読み取りのString.Emptyままにすると、プログラムは同じ文字列を1つの場所でのみ保持して読み取り、または参照する-メモリ内のデータを最小限に抑えます。

また、あなたはconstのようString.Emptyのを使用して、任意のDLLをコンパイルした場合、いかなる理由でString.Emptyを変更し、次にコンパイルしたDLLは、任意のより多くの同じように動作しませんのでcostに内部のコードを作成し、実際に文字列のコピーを保持しますすべての呼び出しで。

たとえば、次のコードを参照してください。

public class OneName
{
    const string cConst = "constant string";
    static string cStatic = "static string";
    readonly string cReadOnly = "read only string";

    protected void Fun()
    {
        string cAddThemAll ;

        cAddThemAll = cConst;
        cAddThemAll = cStatic ;
        cAddThemAll = cReadOnly;    
    }
}

コンパイラーは次のようになります。

public class OneName
{
    // note that the const exist also here !
    private const string cConst = "constant string";
    private readonly string cReadOnly;
    private static string cStatic;

    static OneName()
    {
        cStatic = "static string";
    }

    public OneName()
    {
        this.cReadOnly = "read only string";
    }

    protected void Fun()
    {
        string cAddThemAll ;

        // look here, will replace the const string everywhere is finds it.
        cAddThemAll = "constant string";
        cAddThemAll = cStatic;
        // but the read only will only get it from "one place".
        cAddThemAll = this.cReadOnly;

    }
}

とアセンブリ呼び出し

        cAddThemAll = cConst;
0000003e  mov         eax,dword ptr ds:[09379C0Ch] 
00000044  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cStatic ;
00000047  mov         eax,dword ptr ds:[094E8C44h] 
0000004c  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cReadOnly;
0000004f  mov         eax,dword ptr [ebp-3Ch] 
00000052  mov         eax,dword ptr [eax+0000017Ch] 
00000058  mov         dword ptr [ebp-44h],eax 

編集:タイプミスを修正


つまり、これは、const文字列は常にそのconstを含むクラスでインスタンス化される必要があることを意味しますか?静的な読み取り専用を使用する方がはるかに良いようです。
バーサーカー、2016年

@theberserkerの方が優れていますが、使用できるすべてのオプションがあります。
アリストス2016年

>すると、コンパイルされたdllは同じように機能しなくなります。これは、コストが原因で、呼び出しごとに文字列のコピーを実際に保持するための内部コードが作成されるためです。@アリストスそれは正しくありません。コードがコンパイルされると、文字列の「コピー」は実行可能ファイルのTEXTブロックで参照され、すべてのコードは同じメモリブロックを参照するだけです。2番目のステップで引用したのは、単なる中間ステップです。
Peter Dolkens 16

@ user1533523ありがとうございました
Aristos

どのようにしてそのアセンブリコードを入手しましたか?C#はアセンブリにコンパイルされません!
jv110 2018

0

この答えは歴史的な目的のために存在します。

もともと:

そのためString、クラスであるため、一定とすることができません。

拡張ディスカッション:

この答えを吟味することで多くの有用なダイアログが打ち出されました、そしてそれを削除するのではなく、このコンテンツは直接再現されます:

.NETでは、(Javaとは異なり)文字列と文字列はまったく同じです。そして、はい、.NETで文字列リテラル定数を使用できます– DrJokepu Feb 3 '09 at 16:57

クラスは定数を持つことができないと言っていますか?– StingyJack、2009年2月3日16:58

はい、オブジェクトは読み取り専用を使用する必要があります。構造体だけが定数を実行できます。コンパイラのstring代わりに使用Stringすると、constが読み取り専用に変わります。Cプログラマを満足させるために必要なことはすべて。– Garry Shutler、2009年2月3日16:59

tvanfossonは、もう少し詳細に説明しました。「Xを定数にすることはできません。Yを含むクラスはクラスであるためです」コンテキストに依存していません;)– Leonidas Feb 3 '09 at 17:01

string.Emptyは、Stringクラス自体ではなく、Stringクラスのインスタンス、つまり空の文字列を返す静的プロパティです。– tvanfosson 2009年2月3日17:01

空は、Stringクラスの読み取り専用インスタンスです(プロパティではありません)。– senfo 2009年2月3日17:02

頭が痛い。私はまだ正しいと思いますが、今は確信が持てません。今夜必要な研究!– Garry Shutler、2009年2月3日17:07

空の文字列は文字列クラスのインスタンスです。空は、Stringクラスの静的フィールド(プロパティではなく、修正済みです)です。基本的に、ポインターとそれが指すものの違い。読み取り専用でなければ、Emptyフィールドが参照するインスタンスを変更できます。– tvanfosson 2009年2月3日17:07

ギャリー、調査をする必要はありません。それについて考えてください。文字列はクラスです。空は文字列のインスタンスです。– senfo 2009年2月3日17:12

まったく理解できないことがあるのですが、いったいどのようにしてStringクラスの静的コンストラクターがStringクラスのインスタンスを作成できるのでしょうか。それはある種の「鶏または卵」のシナリオではないでしょうか?– DrJokepu 2009年2月3日17:12 5

この答えは、System.String以外のほぼすべてのクラスに当てはまります。.NETは文字列に対して多くの特別なパフォーマンスケースを実行します。その1つは、文字列定数を使用できることです。試してみてください。この場合、Jeff Yatesが正解です。– Joel Mueller、2009年2月3日19:25

§7.18で説明されているように、定数式はコンパイル時に完全に評価できる式です。string以外の参照型のnull以外の値を作成する唯一の方法は新しい演算子を適用することであり、定数式では新しい演算子を使用できないため、参照型の定数に使用できる唯一の値文字列以外はnullです。前の2つのコメントはC#言語仕様から直接取られたものであり、Joel Muellerが述べたことを繰り返します。– senfo 2月4日09日15:05 5


正しい答えに投票してください。定義に移動すると、それがStringクラスにあり、Stringのインスタンスであることがわかります。小文字で表示されるのはコンパイラーの魔法です。
Garry Shutler、

あなたに反対票を投じたのは私ではありませんでしたが、.NETでは(Javaとは異なり)文字列と文字列はまったく同じです。そして、はい、.NETで文字列リテラル定数を使用できます
Tamas Czinege

10
この答えは、System.String以外のほぼすべてのクラスに当てはまります。.NETは文字列に対して多くの特別なパフォーマンスケースを実行します。その1つは、文字列定数を使用できることです。試してみてください。この場合、Jeff Yatesが正解です。
Joel Mueller、

7
より良い答えが出てきたので、この答えはほぼ削除しましたが、これらのコメントでの議論は維持する価値があります。
Garry Shutler、

1
@ギャリー、あなたは私があなたの最後のコメントを読んで幸運です、そうでなければ私も反対票を投じるでしょう。文字列には.NETで特別な機能があります。そのイベントは、constになることができるrefクラスです。
Shimmy Weitzhandler、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.