Quelle est la différence entre String.Empty et «» (chaîne vide)?


288

Dans .NET, quelle est la différence entre String.Emptyet "", et sont-ils interchangeables, ou existe-t-il des problèmes de référence ou de localisation sous-jacents autour de l'égalité qui String.Emptygarantiront que ce n'est pas un problème?


2
La vraie question n'est PAS quoi plutôt pourquoi . Pourquoi Microsoft a-t-il inventé string.Emptyet quelle était la raison d'être de le déclarer au readonlylieu de const.
user1451111

Réponses:


294

Dans .NET avant la version 2.0, ""crée un objet tout en string.Emptyne créant aucune référence d' objet , ce qui rend string.Emptyplus efficace.

Dans la version 2.0 et les versions ultérieures de .NET, toutes les occurrences de ""font référence au même littéral de chaîne, ce qui signifie qu'il ""est équivalent à .Empty, mais pas aussi rapide que .Length == 0.

.Length == 0est l'option la plus rapide, mais .Emptypermet un code légèrement plus propre.

Voir la spécification .NET pour plus d'informations .


91
"" n'aurait créé un objet qu'une seule fois de toute façon en raison de l'internement de chaînes. Fondamentalement, le compromis de performance est d'arachides - la lisibilité est plus importante.
Jon Skeet

12
Intéressant que , même si la question ne dit rien sur la comparaison d' une chaîne à « » ou string.Empty pour vérifier les chaînes vides, beaucoup de gens semblent interpréter la question de cette façon ...
peSHIr

11
Je serais prudent avec .Length == 0, car il peut lever une exception si votre variable de chaîne est nulle. Cependant, si vous le comparez à "", il retournera correctement false sans aucune exception.
Jeffrey Harmon

11
@JeffreyHarmon: Ou vous pouvez utiliser string.IsNullOrEmpty( stringVar ).
Flynn1179

1
Si quelqu'un souhaite empêcher les mauvaises pratiques de tester une chaîne vide, vous pouvez activer CA1820 dans l'analyse de code. docs.microsoft.com/visualstudio/code-quality/…
sean

197

quelle est la différence entre String.Empty et "", et sont-ils interchangeables

string.Emptyest un champ en lecture seule alors qu'il ""s'agit d'une constante de temps de compilation. Les endroits où ils se comportent différemment sont:

Valeur de paramètre par défaut en C # 4.0 ou supérieur

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

Expression de casse dans l'instruction switch

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

    case "":
        break;

}

Arguments d'attribut

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

21
Intéressant, je ne pensais pas que cette vieille question obtiendrait de nouvelles informations pertinentes. J'avais tort
johnc

Je pense que votre exemple # 1 de valeur de paramètre par défaut en C # 4.0 ou supérieur est essentiellement un doublon de votre exemple # 3 d' arguments d'attribut , car je crois que .NET déploie les paramètres par défaut dans des attributs. Donc , plus fondamentalement, vous ne pouvez pas mettre un (exécution) « valeur » (dans ce cas, une instance poignée , pour les rigoristes là - bas) dans (compilation) métadonnées.
Glenn Slayden

@GlennSlayden, je ne suis pas d'accord avec vous. L'initialisation des attributs est différente d'une initialisation ordinaire. C'est parce que vous pouvez passer l' String.Emptyargument comme argument dans la plupart des cas. Vous avez raison que tous les exemples le montrent you simply can't put a (run-time) "value" into (compile-time) metadataet c'est ce que les exemples visent à montrer.
Sasuke Uchiha

42

Les réponses précédentes étaient correctes pour .NET 1.1 (regardez la date du message qu'ils ont lié: 2003). À partir de .NET 2.0 et versions ultérieures, il n'y a essentiellement aucune différence. Le JIT finira par référencer le même objet sur le tas de toute façon.

Selon la spécification C #, section 2.4.4.5: http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

Chaque littéral de chaîne n'entraîne pas nécessairement une nouvelle instance de chaîne. Lorsque deux ou plusieurs littéraux de chaîne équivalents selon l'opérateur d'égalité de chaîne (Section 7.9.7) apparaissent dans le même assemblage, ces littéraux de chaîne font référence à la même instance de chaîne.

Quelqu'un le mentionne même dans les commentaires du post de Brad Abram

En résumé, le résultat pratique de "" par rapport à String.Empty est nul. Le JIT le découvrira à la fin.

J'ai trouvé, personnellement, que le JIT est bien plus intelligent que moi et j'essaie donc de ne pas devenir trop intelligent avec des optimisations de micro-compilateurs comme ça. Le JIT se déroulera pour les boucles (), supprimera le code redondant, les méthodes en ligne, etc. mieux et à des moments plus appropriés que moi ou le compilateur C # n'aurais jamais pu prévoir auparavant. Laissez le JIT faire son travail :)


1
Petite faute de frappe? "Le JIT finira de toute façon par référencer le même objet dans l'aide." Voulez-vous dire "sur le tas"?
Dana


36

String.Emptyest un champ en lecture seule tandis que ""est un const . Cela signifie que vous ne pouvez pas l'utiliser String.Emptydans une instruction switch car ce n'est pas une constante.


avec l'avènement du defaultmot - clé, nous pouvons promouvoir la lisibilité sans, empêcher les modifications accidentelles et avoir une constante de compilation, mais pour être honnête, je pense toujours que String.Empty est plus lisible que par défaut, mais plus lent à taper
Enzoaeneas

18

Une autre différence est que String.Empty génère un code CIL plus volumineux. Alors que le code pour référencer "" et String.Empty est de la même longueur, le compilateur n'optimise pas la concaténation de chaînes (voir le blog d' Eric Lippert ) pour les arguments String.Empty. Les fonctions équivalentes suivantes

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

générer cet 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 
}

Point valable mais qui ferait une telle chose? Pourquoi devrais-je concaténer une chaîne vide à quelque chose exprès?
Robert S.

@RobertS. Peut-être que la deuxième chaîne était dans une fonction distincte qui a été intégrée. C'est rare, je vous l'accorde.
Bruno Martinez

3
ce n'est pas si rare en utilisant l'opérateur ternaire:"bar " + (ok ? "" : "error")
symbionte

13

Les réponses ci-dessus sont techniquement correctes, mais ce que vous voudrez peut-être vraiment utiliser, pour une meilleure lisibilité du code et moins de risques d'exception, c'est String.IsNullOrEmpty (s)


3
En termes de comparaison d'égalité, je suis tout à fait d'accord, mais la question portait également sur la différence entre les deux concepts ainsi que sur la comparaison
johnc

1
Gardez à l'esprit que «la moindre chance d'une exception» signifie souvent «la plus grande chance de continuer mais de faire quelque chose de mal». Par exemple, si vous avez un argument de ligne de commande que vous analysez et que quelqu'un appelle votre application, --foo=$BARvous souhaiterez probablement faire la différence entre le fait d'oublier de définir une variable env et celui de ne pas passer le drapeau du tout. string.IsNullOrEmptyest souvent une odeur de code que vous n'avez pas validé correctement vos entrées ou que vous faites des choses étranges. Vous ne devriez généralement pas accepter de chaînes vides lorsque vous comptez vraiment les utiliser null, ou quelque chose comme un type Maybe / Option.
Alastair Maw

11

J'ai tendance à utiliser String.Emptyplutôt que ""pour une raison simple, mais pas évidente: ""et ne ""sont pas les mêmes, le premier a en fait 16 caractères de largeur nulle. Évidemment, aucun développeur compétent ne mettra de caractères de largeur nulle dans leur code, mais s'ils y parviennent, cela peut être un cauchemar de maintenance.

Remarques:

  • J'ai utilisé U + FEFF dans cet exemple.

  • Je ne sais pas si SO va manger ces caractères, mais essayez-le vous-même avec l'un des nombreux caractères de largeur nulle

  • Je ne l'ai découvert que grâce à https://codegolf.stackexchange.com/


Cela mérite plus de votes positifs. J'ai vu ce problème se produire via l'intégration du système sur toutes les plates-formes.
EvilDr

8

Utilisez String.Emptyplutôt que "".

C'est plus pour la vitesse que pour l'utilisation de la mémoire, mais c'est un conseil utile. Le ""est un littéral et agira donc comme un littéral: lors de la première utilisation, il est créé et pour les utilisations suivantes, sa référence est renvoyée. Une seule instance de ""sera stockée en mémoire, peu importe combien de fois nous l'utilisons! Je ne vois aucune pénalité de mémoire ici. Le problème est que chaque fois que le ""est utilisé, une boucle de comparaison est exécutée pour vérifier si le ""est déjà dans le pool interne. De l'autre côté, String.Empty est une référence à un ""stocké dans la zone de mémoire .NET Framework . String.Emptypointe vers la même adresse mémoire pour les applications VB.NET et C #. Alors pourquoi chercher une référence à chaque fois que vous en avez besoin ?"" lorsque vous avez cette référence dansString.Empty

Référence: String.Emptyvs""


2
Cela n'a plus été le cas depuis .net 2.0
nelsontruran

6

String.Empty ne crée pas d'objet contrairement à "". La différence, comme indiqué ici , est cependant insignifiante.


4
Ce n'est pas anodin si vous vérifiez une chaîne pour string.Empty ou "". Il faut vraiment utiliser String.IsNullOrEmpty comme l'a souligné Eugene Katz. Sinon, vous obtiendrez des résultats inattendus.
Sigur

6

Toutes les instances de "" sont les mêmes, littérales de chaînes internes (ou elles devraient l'être). Donc, vous ne lancerez vraiment pas un nouvel objet sur le tas chaque fois que vous utilisez "", mais vous créerez simplement une référence au même objet interné. Cela dit, je préfère la chaîne vide. Je pense que cela rend le code plus lisible.



4
string mystring = "";
ldstr ""

ldstr envoie une nouvelle référence d'objet à un littéral de chaîne stocké dans les métadonnées.

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

ldsfld pousse la valeur d'un champ statique sur la pile d'évaluation

J'ai tendance à utiliser à la String.Emptyplace de ""parce qu'à mon humble avis, c'est plus clair et moins VB-ish.


3

Eric Lippert a écrit (17 juin 2013):
"Le premier algorithme sur lequel j'ai travaillé dans le compilateur C # était l'optimiseur qui gère les concaténations de chaînes. Malheureusement, je n'ai pas réussi à porter ces optimisations sur la base de code de Roslyn avant de partir; j'espère que quelqu'un le fera allez-y! "

Voici quelques résultats de Roslyn x64 en janvier 2019. Malgré les remarques consensuelles des autres réponses sur cette page, il ne me semble pas que le JIT x64 actuel traite tous ces cas de manière identique, quand tout est dit et fait.

Notez en particulier, cependant, qu'un seul de ces exemples finit par appeler String.Concat, et je suppose que c'est pour des raisons de correction obscures (par opposition à une surveillance d'optimisation). Les autres différences semblent plus difficiles à expliquer.


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


Détails du test

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

2

En venant à cela d'un point de vue Entity Framework: EF versions 6.1.3 semble traiter String.Empty et "" différemment lors de la validation.

string.Empty est traité comme une valeur nulle à des fins de validation et générera une erreur de validation s'il est utilisé dans un champ Obligatoire (attribué); où "" passera la validation et ne générera pas l'erreur.

Ce problème peut être résolu dans EF 7+. Référence: - https://github.com/aspnet/EntityFramework/issues/2610 ).

Modifier: [Obligatoire (AllowEmptyStrings = true)] résoudra ce problème, permettant à string.Empty de valider.


2

Puisque String.Empty n'est pas une constante de compilation, vous ne pouvez pas l'utiliser comme valeur par défaut dans la définition de fonction.

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

1
Juste le résumé de cette réponse: public void test(int i=0, string s=string.Empty) {}ne compilera pas et ne dira pas "La valeur par défaut du paramètre pour 's' doit être une constante de temps de compilation. La réponse d'OP fonctionne.
bugybunny

1

Lorsque vous scannez visuellement le code, "" apparaît colorisé comme les chaînes sont colorisées. string.Empty ressemble à un accès régulier aux membres de la classe. Lors d 'un rapide coup d' œil, il est plus facile de "" repérer ou de comprendre le sens.

Repérez les chaînes (la colorisation du débordement de la pile n'aide pas vraiment, mais en VS, c'est plus évident):

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

2
Vous faites valoir un bon argument, mais le développeur voulait-il vraiment dire ""? Peut-être voulaient-ils mettre une valeur encore inconnue et ont-ils oublié de revenir? string.Empty a l'avantage de vous donner l'assurance que l'auteur original voulait vraiment dire string.Empty. C'est un point mineur, je sais.
mark_h

En outre, un développeur malveillant (ou imprudent) peut mettre un caractère de largeur nulle entre les guillemets au lieu d'utiliser la séquence d'échappement appropriée comme \u00ad.
Palec

-8

Tout le monde ici a donné une bonne clarification théorique. J'avais un doute similaire. J'ai donc essayé un codage de base dessus. Et j'ai trouvé une différence. Voici la différence.

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


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

Il semble donc que «Null» signifie absolument nul et «String.Empty» signifie qu'il contient une sorte de valeur, mais qu'il est vide.


7
Notez que la question porte sur ""versus string.Empty. Ce n'est qu'en essayant de dire si la chaîne est vide que cela nulla été mentionné.
Palec
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.