String.Emptyと“”(空の文字列)の違いは何ですか?


288

.NETでは、どのような違いであるString.Emptyとは""、彼らは交換可能であるか、平等の周りのいくつかの基礎となる参照またはローカライゼーションの問題がありString.Empty、問題ないことを確認しますか?


2
本当の問題はむしろ何故かということではありません。マイクロソフトが思いついた理由とstring.Empty、それをのreadonly代わりに宣言する理由は何でしたかconst
user1451111 2018

回答:


294

バージョン2.0より前の.NETでは、""オブジェクトをstring.Empty作成し、オブジェクト参照を作成しないため、string.Emptyより効率的です。

.NETバージョン2.0以降では、はすべて""同じ文字列リテラルを参照します。つまり、""と同等ですが.Empty、ほど高速ではありません.Length == 0

.Length == 0は最速のオプションですが、.Emptyコードが少しすっきりします。

詳細については、.NET仕様を参照してください


91
""は、文字列のインターンにより、いずれにせよオブジェクトを一度しか作成しませんでした。基本的に、パフォーマンスのトレードオフはピーナッツです。読みやすさがより重要です。
Jon Skeet、

12
興味深いことに、質問では文字列を ""またはstringと比較することについて何も言われていません。空の文字列を確認するために空にすると、多くの人が質問をそのように解釈するようです...
peSHIr

11
文字列変数がnullの場合に例外がスローされる可能性があるため、.Length == 0には注意します。「」に対してチェックすると、例外なく例外が正しく返されます。
Jeffrey Harmon、2015

11
@JeffreyHarmon:またはを使用できますstring.IsNullOrEmpty( stringVar )
Flynn1179 2015

1
空の文字列をテストするための不正行為を防止したい場合は、コード分析でCA1820を有効にできます。docs.microsoft.com/visualstudio/code-quality/...
ショーン・

197

String.Emptyと ""の違いは何ですか、それらは交換可能ですか

string.Emptyは読み取り専用フィールド""ですが、コンパイル時定数です。動作が異なる場所は次のとおりです。

C#4.0以降のデフォルトパラメータ値

void SomeMethod(int ID, string value = string.Empty)
// Error: Default parameter value for 'value' must be a compile-time constant
{
    //... implementation
}

switchステートメントのcase式

string str = "";
switch(str)
{
    case string.Empty: // Error: A constant value is expected. 
        break;

    case "":
        break;

}

属性引数

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type

21
興味深いことに、この古い質問で関連する新しい情報が得られるとは思いませんでした。私は間違っていました
johnc

私は.NETがデフォルトのパラメーターを属性にデプロイすると信じているため、C#4.0以降の例#1のデフォルトパラメーター値は、例#3の属性引数と本質的に同じです。したがって、より基本的には、(実行時)の「値」(この場合は、そこにいるステッカーのインスタンスハンドル)を(コンパイル時)メタデータに入れることはできません。
Glenn Slayden

@GlennSlayden、私はあなたに同意しません。属性の初期化は、通常の初期化とは異なります。これはString.Empty、ほとんどの場合、引数としてを渡すことができるためです。すべての例がそれyou simply can't put a (run-time) "value" into (compile-time) metadataを示しており、それが例が示すことを目指しているものです。
うちはサスケ

42

以前の回答は.NET 1.1に正しかった(彼らがリンクした投稿の日付を見てください:2003)。.NET 2.0以降では、本質的に違いはありません。とにかく、JITはヒープ上の同じオブジェクトを参照することになります。

C#仕様のセクション2.4.4.5によると:http ://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

各文字列リテラルは、必ずしも新しい文字列インスタンスになるわけではありません。文字列等価演算子(7.9.7節)に従って同等の2つ以上の文字列リテラルが同じアセンブリ内にある場合、これらの文字列リテラルは同じ文字列インスタンスを参照します。

誰かがブラッド・エイブラムの投稿のコメントでこれについても言及しています

要約すると、 ""とString.Emptyの実際の結果はnilです。JITは最終的にそれを理解します。

私は個人的に、JITが私よりもはるかに賢いので、そのようなマイクロコンパイラの最適化をあまり賢くしないようにしています。JITはfor()ループを展開し、冗長なコード、インラインメソッドなどを、IまたはC#コンパイラーが事前に予想できるよりも適切かつ適切なタイミングで削除します。JITにその仕事を任せましょう:)


1
誤字?「JITはとにかくヘルプの同じオブジェクトを参照することになります。」「ヒープ上」という意味ですか?
ダナ


36

String.Empty読み取り専用フィールド""ですが、constです。つまりString.Empty、定数ではないため、switchステートメントでは使用できません。


defaultキーワードの出現により、偶然の変更を防ぎ、偶然の変更を防ぎ、コンパイル時の定数を持たせることができるようになりましたが、正直なところ、String.Emptyはデフォルトより読みやすいと思いますが、入力が遅い
Enzoaeneas

18

もう1つの違いは、String.Emptyがより大きなCILコードを生成することです。""とString.Emptyを参照するコードは同じ長さですが、コンパイラーはString.Empty引数の文字列連結(Eric Lippertのブログ投稿を参照)を最適化しません。以下の同等の機能

string foo()
{
    return "foo" + "";
}
string bar()
{
    return "bar" + string.Empty;
}

このILを生成する

.method private hidebysig instance string foo() cil managed
{
    .maxstack 8
    L_0000: ldstr "foo"
    L_0005: ret 
}
.method private hidebysig instance string bar() cil managed
{
    .maxstack 8
    L_0000: ldstr "bar"
    L_0005: ldsfld string [mscorlib]System.String::Empty
    L_000a: call string [mscorlib]System.String::Concat(string, string)
    L_000f: ret 
}

有効なポイントですが、誰がそのようなことをするでしょうか?空の文字列をわざと何かに連結する必要があるのはなぜですか?
Robert S.17年

@RobertS。たぶん、2番目の文字列はインライン化された別の関数に含まれていました。珍しいですよ。
Bruno Martinez

3
それは三項演算子を使用して、その稀ではありません:"bar " + (ok ? "" : "error")
共生

13

上記の答えは技術的には正しいですが、コードを読みやすくし、例外が発生する可能性を最小限にするために、実際に使用したいのはString.IsNullOrEmpty(s)です。


3
同等性の比較に関しては、私は完全に同意しますが、問題は、2つの概念の違いと比較についても同様
でした

1
「例外が発生する可能性が最も低い」とは、「継続するが、何か間違っている可能性が最も高い」ことを意味する場合があることに注意してください。たとえば、解析しているコマンドライン引数があり、誰かがあなたのアプリを呼び出す--foo=$BAR場合、おそらく環境変数を設定し忘れていることと、フラグをまったく渡していないことの違いを見つけたいでしょう。string.IsNullOrEmpty多くの場合、入力を適切に検証していないか、奇妙なことをしているコード臭です。実際にnull、またはMaybe / Optionタイプのようなものを使用するつもりである場合、通常は空の文字列を受け入れるべきではありません。
Alastair Maw 2018年

11

私が使用する傾向があるString.Emptyのではなくを""1つの簡単なため、まだ明らかでない理由: """"同じではありません、最初のものは実際にそれに16個のゼロ幅文字を持っています。明らかに、有能な開発者がコードに幅ゼロの文字を入れようとはしていませんが、そこに入ると、メンテナンスの悪夢になる可能性があります。

ノート:


これは、より多くの賛成投票に値します。この問題は、プラットフォーム間のシステム統合を介して発生するのを見てきました。
EvilDr 2018

8

String.Emptyよりも使用してください""

これはメモリ使用量よりも速度の点で優れていますが、役立つヒントです。 ""リテラルのでリテラルとして機能します次のとおりです。最初の使用時にそれが作成され、以下の用途のためにその参照が返されます。""何回使用しても、に格納されるのは1つのインスタンスのみです。ここにはメモリのペナルティはありません。問題は、""が使用されるたびに、比較ループが実行され、""すでにがインターンプールにあるかどうかをチェックすること です。一方、は、.NET Frameworkのメモリゾーンに格納されているString.Empty への参照です。 VB.NETとC#アプリケーションの同じメモリアドレスを指しています。それで、 その参照があるときに必要になるたびに参照を検索する理由""String.Empty""String.Empty

リファレンス:String.Emptyvs""


2
これは.net 2.0以降は当てはまりません
nelsontruran

6

String.Emptyはオブジェクトを作成しませんが、 ""は作成します。ただし、ここで指摘したように、その違いはごくわずかです。


4
文字列のstring.Emptyまたは ""をチェックするのは簡単ではありません。Eugene Katzが指摘したように、実際にはString.IsNullOrEmptyを使用する必要があります。そうしないと、予期しない結果になります。
Sigur 14

6

""のすべてのインスタンスは、同じインターン文字列リテラルです(またはそうである必要があります)。したがって、 ""を使用するたびにヒープに新しいオブジェクトをスローするのではなく、同じインターンオブジェクトへの参照を作成するだけです。そうは言っても、私はstring.Emptyを好みます。コードが読みやすくなると思います。



4
string mystring = "";
ldstr ""

ldstr メタデータに保存されている文字列リテラルへの新しいオブジェクト参照をプッシュします。

string mystring = String.Empty;
ldsfld string [mscorlib]System.String::Empty

ldsfld 静的フィールドの値を評価スタックにプッシュします

私はString.Empty代わりに使用する傾向があります。""なぜなら私見はそれがより明確でVBっぽさが少ないからです。


3

Eric Lippert は次のように書いています(2013年6月17日):
「C#コンパイラで最初に取り組んだアルゴリズムは文字列連結を処理するオプティマイザでした。残念ながら、去る前にこれらの最適化をRoslynコードベースに移植できませんでした。うまくいけば誰かがそれに着く!

2019年1月現在のRoslyn x64の結果をいくつか示します。このページの他の回答についての意見の一致にもかかわらず、現在のx64 JITがこれらすべてのケースを同じように扱っているとは思えません。

ただし、特にこれらの例の1つだけが実際にを呼び出すことString.Concatに注意してください。これは、(最適化の監視とは対照的に)正確性の理由がわかりにくいためだと思います。他の違いは説明するのが難しいようです。


default(String)+ {default(String)、 ""、String.Empty}

static String s00() => default(String) + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s01() => default(String) + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s02() => default(String) + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

"" + {default(String)、 ""、String.Empty}

static String s03() => "" + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s04() => "" + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s05() => "" + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

String.Empty + {default(String)、 ""、String.Empty}

static String s06() => String.Empty + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s07() => String.Empty + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s08() => String.Empty + String.Empty;
    mov  rcx,[String::Empty]
    mov  rcx,qword ptr [rcx]
    mov  qword ptr [rsp+20h],rcx
    mov  rcx,qword ptr [rsp+20h]
    mov  rdx,qword ptr [rsp+20h]
    call F330CF60                 ; <-- String.Concat
    nop
    add  rsp,28h
    ret


テストの詳細

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false

2

Entity Frameworkの観点から見ると、EFバージョン6.1.3は検証時にString.Emptyと ""を異なる方法で処理するように見えます。

string.Emptyは検証の目的でnull値として扱われ、必須(属性付き)フィールドで使用された場合、検証エラーをスローします。ここで、 ""は検証に合格し、エラーをスローしません。

この問題はEF 7以降で解決される可能性があります。参照:-https : //github.com/aspnet/EntityFramework/issues/2610)。

編集:[必須(AllowEmptyStrings = true)]はこの問題を解決し、string.Emptyが検証できるようにします。


2

String.Emptyはコンパイル時の定数ではないため、関数定義のデフォルト値として使用することはできません。

public void test(int i=0,string s="")
    {
      // Function Body
    }

1
この回答からわずか要約:public void test(int i=0, string s=string.Empty) {}コンパイルと「『S』のデフォルトパラメータ値がコンパイル時定数でなければなりません言うことはありませんOPの答えの作品を。。
bugybunny

1

コードを視覚的にスキャンしているとき、 ""は文字列の色分けと同じように色分けされて表示されます。string.Emptyは、通常のクラスメンバーアクセスのように見えます。一見すると、「」を見つけたり、意味を直感的に理解したりするのが簡単になります。

文字列を見つけます(スタックオーバーフローの色付けは正確には役に立ちませんが、VSではこれはより明白です):

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;

2
あなたは良い点を作りましたが、開発者は本当に「」を意味しましたか?おそらく、彼らはまだ未知の値をいくつか入れて、戻るのを忘れていたのでしょうか?string.Emptyには、元の作者が本当にstring.Emptyを意味していたという確信を与えるという利点があります。それは私の知っているマイナーなポイントです。
mark_h

また、悪意のある(または不注意な)開発者は、のような適切なエスケープシーケンスを使用する代わりに、引用符の間にゼロ幅の文字を挿入する場合があります\u00ad
Palec、

-8

ここの誰もがいくつかの良い理論的な説明を与えました。私にも同様の疑いがありました。だから私はそれに基本的なコーディングを試みました。そして、私は違いを見つけました。ここに違いがあります。

string str=null;
Console.WriteLine(str.Length);  // Exception(NullRefernceException) for pointing to null reference. 


string str = string.Empty;
Console.WriteLine(str.Length);  // 0

したがって、「Null」は完全に無効であることを意味し、「String.Empty」は何らかの値が含まれていることを意味しますが、空です。


7
問題は""対についてであることに注意してくださいstring.Empty。文字列が空であるかどうかを確認しようとしたときにのみ、null言及されていました。
Palec、2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.