Was ist der Unterschied zwischen String.Empty und "" (leerer String)?


288

Was ist in .NET der Unterschied zwischen String.Emptyund ""und sind sie austauschbar, oder gibt es einige zugrunde liegende Referenz- oder Lokalisierungsprobleme im Zusammenhang mit der Gleichstellung, String.Emptydie sicherstellen, dass sie kein Problem darstellen?


2
Die eigentliche Frage ist NICHT was eher warum . Warum Microsoft darauf gekommen ist string.Emptyund was der Grund dafür war, es als readonlystatt zu deklarieren const.
user1451111

Antworten:


294

In .NET vor Version 2.0, ""erstellt ein Objekt , während string.Emptykein Objekt erstellt ref , was macht string.Emptyeffizienter.

In Version 2.0 und höher von .NET ""beziehen sich alle Vorkommen von auf dasselbe Zeichenfolgenliteral, was bedeutet, dass ""es äquivalent zu .Empty, aber immer noch nicht so schnell ist wie .Length == 0.

.Length == 0ist die schnellste Option, .Emptysorgt aber für etwas saubereren Code.

Weitere Informationen finden Sie in der .NET-Spezifikation .


91
"" hätte aufgrund der String-Internierung ohnehin nur einmal ein Objekt erstellt. Grundsätzlich ist der Leistungskompromiss Erdnüsse - Lesbarkeit ist wichtiger.
Jon Skeet

12
Interessant, dass, obwohl die Frage nichts über den Vergleich einer Zeichenfolge mit "" oder Zeichenfolge aussagt. Es ist nicht möglich, nach leeren Zeichenfolgen zu suchen, viele Leute die Frage so zu interpretieren ...
peSHIr

11
Ich würde mit .Length == 0 vorsichtig sein, da es eine Ausnahme auslösen kann, wenn Ihre Zeichenfolgenvariable null ist. Wenn Sie es jedoch mit "" vergleichen, wird nur ausnahmslos false zurückgegeben.
Jeffrey Harmon

11
@ JeffreyHarmon: Oder Sie könnten verwenden string.IsNullOrEmpty( stringVar ).
Flynn1179

1
Wenn jemand verhindern möchte, dass schlechte Praktiken auf eine leere Zeichenfolge getestet werden, können Sie CA1820 in Code Analysis aktivieren. docs.microsoft.com/visualstudio/code-quality/…
Sean

197

Was ist der Unterschied zwischen String.Empty und "" und sind sie austauschbar?

string.Emptyist ein schreibgeschütztes Feld, während ""es sich um eine Kompilierungszeitkonstante handelt. Orte, an denen sie sich anders verhalten, sind:

Standardparameterwert in C # 4.0 oder höher

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

Groß- / Kleinschreibung in der switch-Anweisung

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

    case "":
        break;

}

Attributargumente

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

21
Interessanterweise hätte ich nicht gedacht, dass diese alte Frage relevante neue Informationen erhalten würde. Ich habe mich geirrt
Johnc

Ich denke, Ihr Standardparameterwert für Beispiel 1 in C # 4.0 oder höher ist im Wesentlichen ein Duplikat Ihrer Attributargumente für Beispiel 3 , da ich glaube, dass .NET die Standardparameter in Attributen bereitstellt. Also mehr im Grunde kann man einfach nicht setzt eine (Laufzeit) „Wert“ (in diesem Fall eine Instanz handelt , für die sticklers da draußen) in (Compile-Zeit) Metadaten.
Glenn Slayden

@GlennSlayden, ich stimme dir nicht zu. Die Attributinitialisierung unterscheidet sich von einer normalen Initialisierung. Das liegt daran, dass Sie das String.Emptyin den meisten Fällen als Argument übergeben können. Sie haben Recht, dass alle Beispiele dies zeigen, you simply can't put a (run-time) "value" into (compile-time) metadataund das ist es, was die Beispiele zeigen sollen.
Sasuke Uchiha

42

Die vorherigen Antworten waren für .NET 1.1 korrekt (siehe Datum des verlinkten Beitrags: 2003). Ab .NET 2.0 und höher gibt es im Wesentlichen keinen Unterschied. Die JIT verweist ohnehin auf dasselbe Objekt auf dem Heap.

Gemäß der C # -Spezifikation, Abschnitt 2.4.4.5: http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

Jedes String-Literal führt nicht unbedingt zu einer neuen String-Instanz. Wenn zwei oder mehr Zeichenfolgenliterale, die dem Zeichenfolgengleichheitsoperator (Abschnitt 7.9.7) entsprechen, in derselben Assembly angezeigt werden, beziehen sich diese Zeichenfolgenliterale auf dieselbe Zeichenfolgeninstanz.

Jemand erwähnt dies sogar in den Kommentaren von Brad Abrams Beitrag

Zusammenfassend ist das praktische Ergebnis von "" vs. String.Empty gleich Null. Die JIT wird es am Ende herausfinden.

Ich persönlich habe festgestellt, dass die JIT viel schlauer ist als ich, und deshalb versuche ich, mit solchen Micro-Compiler-Optimierungen nicht zu schlau zu werden. Die JIT wird sich für () -Schleifen entfalten, redundanten Code, Inline-Methoden usw. besser und zu angemesseneren Zeiten entfernen, als ich oder der C # -Compiler jemals zuvor erwartet hätten. Lass die JIT ihren Job machen :)


1
Kleiner Tippfehler? "Die JIT verweist ohnehin auf dasselbe Objekt in der Hilfe." Meinten Sie "auf dem Haufen"?
Dana


36

String.Emptyist ein Nur - Lese - während - Feld ""ist ein const . Dies bedeutet, dass Sie String.Emptyeine switch-Anweisung nicht verwenden können , da sie keine Konstante ist.


Mit dem Aufkommen des defaultSchlüsselworts können wir die Lesbarkeit fördern, versehentliche Änderungen verhindern und eine Konstante für die Kompilierungszeit haben. Um ehrlich zu sein, denke ich immer noch, dass String.Empty besser lesbar als Standard ist, aber langsamer zu tippen
Enzoaeneas

18

Ein weiterer Unterschied besteht darin, dass String.Empty einen größeren CIL-Code generiert. Während der Code für die Referenzierung von "" und String.Empty gleich lang ist, optimiert der Compiler die String-Verkettung (siehe Eric Lipperts Blog-Beitrag ) nicht für String.Empty-Argumente. Die folgenden äquivalenten Funktionen

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

Generieren Sie diese 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 
}

Gültiger Punkt, aber wer würde so etwas tun? Warum sollte ich eine leere Zeichenfolge absichtlich mit etwas verketten?
Robert S.

@ Roberts. Vielleicht befand sich die zweite Zeichenfolge in einer separaten Funktion, die eingefügt wurde. Es ist selten, das gebe ich dir zu.
Bruno Martinez

3
es ist nicht so selten mit dem ternären Operator:"bar " + (ok ? "" : "error")
Symbiont

13

Die obigen Antworten sind technisch korrekt, aber was Sie wirklich verwenden möchten, um die beste Lesbarkeit des Codes und die geringste Wahrscheinlichkeit einer Ausnahme zu erzielen, ist String.IsNullOrEmpty (s)


3
In Bezug auf den Gleichstellungsvergleich stimme ich voll und ganz zu, aber die Frage
betraf

1
Beachten Sie, dass "geringste Chance auf eine Ausnahme" oft "größte Chance bedeutet, weiterzumachen, aber etwas falsch zu machen". Wenn Sie beispielsweise ein Befehlszeilenargument haben, das Sie analysieren, und jemand Ihre App mit aufruft, --foo=$BARmöchten Sie wahrscheinlich den Unterschied zwischen dem Vergessen, eine env var festzulegen, und dem Nichtübergeben des Flags erkennen. string.IsNullOrEmptyist oft ein Code-Geruch, dass Sie Ihre Eingaben nicht richtig validiert haben oder seltsame Dinge tun. Sie sollten im Allgemeinen keine leeren Zeichenfolgen akzeptieren, wenn Sie diese wirklich verwenden möchten null, oder so etwas wie einen Vielleicht / Option-Typ.
Alastair Maw

11

Ich benutze String.Emptyeher als ""aus einem einfachen, aber nicht offensichtlichen Grund: ""und ""sind NICHT gleich, der erste enthält tatsächlich 16 Zeichen mit einer Breite von Null. Natürlich wird kein kompetenter Entwickler Zeichen mit einer Breite von Null in seinen Code einfügen, aber wenn sie dort ankommen, kann dies ein Alptraum für die Wartung sein.

Anmerkungen:


Dies verdient mehr Gegenstimmen. Ich habe gesehen, dass dieses Problem durch systemübergreifende Systemintegration auftritt.
EvilDr

8

Verwenden Sie String.Emptyeher als "".

Dies ist mehr für die Geschwindigkeit als für die Speichernutzung, aber es ist ein nützlicher Tipp. Das ""ist ein Literal und fungiert daher als Literal: Bei der ersten Verwendung wird es erstellt und für die folgenden Verwendungen wird seine Referenz zurückgegeben. Es wird nur eine Instanz von ""gespeichert, egal wie oft wir sie verwenden! Ich sehe hier keine Gedächtnisstrafen. Das Problem ist, dass jedes Mal "", wenn das verwendet wird, eine Vergleichsschleife ausgeführt wird, um zu überprüfen, ob sich das ""bereits im internen Pool befindet. Auf der anderen Seite String.Empty befindet sich ein Verweis auf eine ""in der .NET Framework-Speicherzone gespeicherte Zone . String.Emptyzeigt auf dieselbe Speicheradresse für VB.NET- und C # -Anwendungen. Warum also jedes Mal nach einer Referenz suchen, "" wenn Sie diese Referenz in haben?String.Empty?

Referenz: String.Emptyvs.""


2
Dies ist seit .net 2.0
Nelsontruran

6

String.Empty erstellt kein Objekt, während "" dies tut. Der hier erwähnte Unterschied ist jedoch trivial.


4
Es ist nicht trivial, wenn Sie eine Zeichenfolge auf Zeichenfolge überprüfen. Leer oder "". Man sollte wirklich String.IsNullOrEmpty verwenden, wie Eugene Katz betonte. Andernfalls erhalten Sie unerwartete Ergebnisse.
Sigur

6

Alle Instanzen von "" sind das gleiche, internierte Zeichenfolgenliteral (oder sollten es sein). Sie werden also nicht jedes Mal, wenn Sie "" verwenden, ein neues Objekt auf den Heap werfen, sondern nur einen Verweis auf dasselbe internierte Objekt erstellen. Trotzdem bevorzuge ich string.Empty. Ich denke, es macht Code lesbarer.



4
string mystring = "";
ldstr ""

ldstr verschiebt einen neuen Objektverweis auf ein in den Metadaten gespeichertes Zeichenfolgenliteral.

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

ldsfld schiebt den Wert eines statischen Feldes auf den Auswertungsstapel

Ich neige dazu, String.Emptyanstatt zu verwenden, ""weil IMHO es klarer und weniger VB-ish ist.


3

Eric Lippert schrieb (17. Juni 2013):
"Der erste Algorithmus, an dem ich jemals im C # -Compiler gearbeitet habe, war der Optimierer, der Zeichenfolgenverkettungen verarbeitet. Leider konnte ich diese Optimierungen nicht in die Roslyn-Codebasis portieren, bevor ich ging. Hoffentlich wird es jemand tun." Komm schon! "

Hier sind einige Roslyn x64- Ergebnisse vom Januar 2019. Trotz der Konsensbemerkungen der anderen Antworten auf dieser Seite scheint es mir nicht, dass die aktuelle x64-JIT alle diese Fälle identisch behandelt, wenn alles gesagt und getan ist.

Beachten Sie jedoch insbesondere, dass nur eines dieser Beispiele tatsächlich aufgerufen wird String.Concat, und ich vermute, dass dies aus Gründen der unklaren Korrektheit geschieht (im Gegensatz zu einem Optimierungsversehen). Die anderen Unterschiede scheinen schwerer zu erklären.


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


Testdetails

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

2

Aus Sicht von Entity Framework: EF-Versionen 6.1.3 scheinen String.Empty und "" bei der Validierung unterschiedlich zu behandeln.

string.Empty wird zu Validierungszwecken als Nullwert behandelt und löst einen Validierungsfehler aus, wenn er in einem erforderlichen (zugewiesenen) Feld verwendet wird. Dabei wird "" die Validierung bestehen und den Fehler nicht auslösen.

Dieses Problem kann in EF 7+ behoben werden. Referenz: - https://github.com/aspnet/EntityFramework/issues/2610 ).

Bearbeiten: [Erforderlich (AllowEmptyStrings = true)] behebt dieses Problem und ermöglicht die Validierung von string.Empty.


2

Da String.Empty keine Konstante zur Kompilierungszeit ist, können Sie sie nicht als Standardwert in der Funktionsdefinition verwenden.

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

1
Nur die Zusammenfassung dieser Antwort: public void test(int i=0, string s=string.Empty) {}wird nicht kompiliert und sagt "Der Standardparameterwert für 's' muss eine Konstante für die Kompilierungszeit sein. Die Antwort von OP funktioniert.
bugybunny

1

Wenn Sie Code visuell durchsuchen, wird "" so eingefärbt angezeigt, wie Zeichenfolgen eingefärbt sind. string.Empty sieht aus wie ein normaler Zugriff auf Klassenmitglieder. Während eines kurzen Blicks ist es einfacher, die Bedeutung zu erkennen oder zu verstehen.

Finde die Zeichenfolgen (die Stapelüberlauffärbung hilft nicht gerade, aber in VS ist dies offensichtlicher):

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

2
Sie machen einen guten Punkt, aber meinte der Entwickler wirklich ""? Vielleicht wollten sie einen noch unbekannten Wert eingeben und vergaßen zurückzukehren? string.Empty hat den Vorteil, dass Sie sicher sein können, dass der ursprüngliche Autor string.Empty wirklich gemeint hat. Es ist ein kleiner Punkt, den ich kenne.
mark_h

Außerdem kann ein böswilliger (oder nachlässiger) Entwickler ein Zeichen mit der Breite Null zwischen die Anführungszeichen setzen, anstatt die entsprechende Escape-Sequenz wie zu verwenden \u00ad.
Palec

-8

Alle hier gaben eine gute theoretische Erklärung. Ich hatte einen ähnlichen Zweifel. Also habe ich eine grundlegende Codierung versucht. Und ich habe einen Unterschied gefunden. Hier ist der Unterschied.

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


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

Es scheint also, dass "Null" absolut nichtig bedeutet und "String.Empty" bedeutet, dass es einen Wert enthält, aber leer ist.


7
Beachten Sie, dass es sich bei der Frage um ""Versus handelt string.Empty. Nur beim Versuch festzustellen, ob die Zeichenfolge leer ist, nullwurde erwähnt.
Palec
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.