Qual é a diferença entre String.Empty e “” (string vazia)?

288

No .NET, qual é a diferença entre String.Emptye "", e eles são intercambiáveis, ou há alguns problemas subjacentes de referência ou Localização em torno da igualdade que String.Emptygarantirão que não sejam um problema?

johnc
fonte
2
A verdadeira questão não é o bastante por isso . Por que a Microsoft surgiu string.Emptye qual foi a lógica por trás de declará-la como em readonlyvez de const.
User1451111 07/0718

Respostas:

294

No .NET anterior à versão 2.0, ""cria um objeto enquanto string.Emptynão cria ref de objeto , o que tornastring.Empty mais eficiente.

Na versão 2.0 e posterior do .NET, todas as ocorrências de ""referem-se à mesma string literal, o que significa que ""é equivalente a .Empty, mas ainda não tão rápido quanto.Length == 0 .

.Length == 0 é a opção mais rápida, mas .Empty para um código um pouco mais limpo.

Consulte a especificação do .NET para obter mais informações .

Brian R. Bondy
fonte
91
"" só teria criado um objeto uma vez, devido à internação de strings. Basicamente, a troca de desempenho é o amendoim - a legibilidade é mais importante.
Jon Skeet
12
Interessante que, embora a questão não diz nada sobre a comparação de uma string para "" ou string.Empty para verificar strings vazias, muitas pessoas parecem interpretar a questão dessa maneira ...
peSHIr
11
Eu teria cuidado com .Length == 0, pois isso pode gerar uma exceção se sua variável de string for nula. Se você compará-lo com "", porém, ele retornará falso corretamente, sem exceção.
Jeffrey Harmon
11
@JeffreyHarmon: Ou você poderia usar string.IsNullOrEmpty( stringVar ).
precisa saber é o seguinte
1
Se alguém quiser impedir que práticas inadequadas testem uma cadeia vazia, você pode ativar o CA1820 na Análise de Código. docs.microsoft.com/visualstudio/code-quality/…
sean
197

qual é a diferença entre String.Empty e "" e eles são intercambiáveis

string.Empty é um campo somente leitura, enquanto "" é uma constante de tempo de compilação. Os locais onde eles se comportam de maneira diferente são:

Valor padrão do parâmetro em C # 4.0 ou superior

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

Expressão de caso na instrução switch

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

    case "":
        break;

}

Argumentos de atributo

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type
Habib
fonte
21
Interessante, eu não acho que essa pergunta antiga receba novas informações relevantes. Eu estava errado
johnc 5/12/12
Eu acho que o seu exemplo # 1 valor do parâmetro padrão em C # 4.0 ou superior é essencialmente uma duplicata do seu exemplo # 3 argumentos de atributo , pois acredito que o .NET implanta os parâmetros padrão em atributos. Então, mais basicamente, você simplesmente não pode colocar um "valor" (em tempo de execução) (neste caso, um identificador de instância , para os manipuladores existentes) nos metadados (em tempo de compilação).
Glenn Slayden
@ GlennSlayden, eu não concordo com você. A inicialização do atributo é diferente de uma inicialização comum. Isso ocorre porque você pode passar String.Emptycomo argumento na maioria dos casos. Você está certo que todos os exemplos mostram isso you simply can't put a (run-time) "value" into (compile-time) metadatae é isso que os exemplos pretendem mostrar.
Sasuke Uchiha
42

As respostas anteriores estavam corretas para o .NET 1.1 (veja a data da postagem que eles vincularam: 2003). A partir do .NET 2.0 e posterior, não há essencialmente nenhuma diferença. O JIT acabará referenciando o mesmo objeto no heap de qualquer maneira.

De acordo com a especificação C #, seção 2.4.4.5: http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

Cada literal de cadeia não resulta necessariamente em uma nova instância de cadeia. Quando dois ou mais literais de sequência equivalentes de acordo com o operador de igualdade de sequência (Seção 7.9.7) aparecem no mesmo assembly, esses literais de sequência se referem à mesma instância de sequência.

Alguém até menciona isso nos comentários do post de Brad Abram

Em resumo, o resultado prático de "" vs. String.Empty é nulo. O JIT descobrirá isso no final.

Descobri, pessoalmente, que o JIT é muito mais inteligente do que eu e, por isso, tento não ficar muito esperto com otimizações de micro-compiladores como essa. O JIT se desdobra para loops (), remove código redundante, métodos em linha, etc. melhor e em momentos mais apropriados do que eu ou o compilador C # jamais poderia antecipar antes. Deixe o JIT fazer seu trabalho :)

chadmyers
fonte
1
Erro de digitação pequeno? "O JIT acabará referenciando o mesmo objeto na ajuda de qualquer maneira." Você quis dizer "na pilha"?
Dana
Pergunta popular relacionada stackoverflow.com/questions/263191/…
Michael Freidgeim 31/03
36

String.Emptyé um campo somente leitura enquanto ""é uma const . Isso significa que você não pode usar String.Emptyem uma instrução switch porque não é uma constante.

James Newton-King
fonte
com o advento da defaultpalavra-chave que pode promover a leitura, sem, impedir a modificação acidental, e tem uma constante de tempo de compilação, embora para ser honesto, eu ainda acho String.Empty é mais legível do que padrão, mas mais lento para digitar
Enzoaeneas
18

Outra diferença é que String.Empty gera um código CIL maior. Embora o código para referenciar "" e String.Empty tenha o mesmo comprimento, o compilador não otimiza a concatenação de strings (consulte a publicação no blog de Eric Lippert ) para obter os argumentos String.Empty. As seguintes funções equivalentes

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

gerar essa 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 
}
Bruno Martinez
fonte
Ponto válido, mas quem faria isso? Por que devo concatenar uma string vazia para algo de propósito?
Robert S.
@RobertS. Talvez a segunda string estivesse em uma função separada que foi incorporada. É raro, eu garanto.
Bruno Martinez
3
não é tão raro usar o operador ternário:"bar " + (ok ? "" : "error")
symbiont
13

As respostas acima são tecnicamente corretas, mas o que você realmente deseja usar, para melhor legibilidade do código e menor chance de exceção, é String.IsNullOrEmpty (s)

Eugene Katz
fonte
3
Em termos de comparação de igualdade, eu concordo completamente, mas a questão foi também sobre a diferença entre os dois conceitos, bem como comparação
johnc
1
Cuidado que "a menor chance de uma exceção" geralmente significa "mais chances de continuar, mas fazer algo errado". Por exemplo, se você tem um argumento de linha de comando que está analisando e alguém chama seu aplicativo --foo=$BAR, provavelmente deseja identificar a diferença entre eles esquecerem de definir uma var env e eles não passarem o sinalizador. string.IsNullOrEmptygeralmente é um cheiro de código que você não validou suas entradas corretamente ou está fazendo coisas estranhas. Geralmente, você não deve aceitar cadeias de caracteres vazias quando realmente deseja usar null, ou algo como o tipo Talvez / Opção.
Alastair Maw
11

Costumo usar String.Emptymais do que ""por um motivo simples, mas não óbvio: ""e ""NÃO é o mesmo, o primeiro tem 16 caracteres de largura zero. Obviamente, nenhum desenvolvedor competente colocará zero caracteres de largura em seu código, mas se eles chegarem lá, pode ser um pesadelo de manutenção.

Notas:

  • Eu usei U + FEFF neste exemplo.

  • Não tenho certeza se o SO vai comer esses caracteres, mas tente você mesmo com um dos muitos caracteres de largura zero

  • Só me deparei com isso graças a https://codegolf.stackexchange.com/

The_Lone_Devil
fonte
Isso merece mais votos. Eu já vi esse problema ocorrer através da integração do sistema entre plataformas.
EvilDr
8

Use em String.Emptyvez de "".

Isso é mais por velocidade do que por uso de memória, mas é uma dica útil. Como ""é um literal, ele atuará como um literal: no primeiro uso, ele é criado e, para os seguintes usos, sua referência é retornada. Apenas uma instância de ""será armazenada na memória, não importa quantas vezes a utilizemos! Não vejo nenhuma penalidade de memória aqui. O problema é que, sempre que ""é usado, um loop de comparação é executado para verificar se o ""já está no pool interno. Por outro lado, String.Empty é uma referência a um ""arquivo armazenado na zona de memória do .NET Framework . String.Emptyestá apontando para o mesmo endereço de memória para aplicativos VB.NET e C #. Então, por que procurar uma referência sempre que precisar ?"" quando tiver essa referência emString.Empty

Referência: String.Emptyvs""

Mojtaba Rezaeian
fonte
2
Isso não é verdade desde .net 2.0 #
nelsontruran
6

String.Empty não cria um objeto, enquanto "" cria. A diferença, como apontado aqui , é trivial, no entanto.

Esteban Araya
fonte
4
Não é trivial se você verificar uma string por string.Empty ou "". Deve-se realmente usar String.IsNullOrEmpty como Eugene Katz apontou. Caso contrário, você obterá resultados inesperados.
Sigur 27/02
6

Todas as instâncias de "" são iguais, literal de cadeia interna (ou deveriam ser). Então você realmente não estará lançando um novo objeto na pilha toda vez que usar "", mas apenas criando uma referência ao mesmo objeto interno. Dito isto, eu prefiro string.Empty. Eu acho que torna o código mais legível.

Jason Jackson
fonte
4
string mystring = "";
ldstr ""

ldstr envia uma nova referência de objeto a um literal de seqüência de caracteres armazenado nos metadados.

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

ldsfld coloca o valor de um campo estático na pilha de avaliação

Eu tendem a usar em String.Emptyvez de ""porque IMHO é mais claro e menos VB-ish.

Salvuccino
fonte
3

Eric Lippert escreveu (17 de junho de 2013):
"O primeiro algoritmo em que trabalhei no compilador C # foi o otimizador que lida com concatenações de cadeias. Infelizmente, não consegui portar essas otimizações para a base de código de Roslyn antes de sair; espero que alguém chegar a isso! "

Aqui estão alguns resultados do Roslyn x64 a partir de janeiro de 2019. Apesar das observações consensuais das outras respostas nesta página, não me parece que o JIT x64 atual esteja tratando todos esses casos de forma idêntica, quando tudo estiver dito e feito.

Observe, em particular, que apenas um desses exemplos acaba chamando String.Concat, e acho que isso é por motivos obscuros de correção (em oposição a uma supervisão de otimização). As outras diferenças parecem mais difíceis de explicar.


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

"" + {padrão (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 + {padrão (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


Detalhes do teste

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

Chegando a isso do ponto de vista do Entity Framework: o EF versões 6.1.3 parece tratar String.Empty e "" de maneira diferente ao validar.

string.Empty é tratado como um valor nulo para fins de validação e lançará um erro de validação se for usado em um campo Obrigatório (atribuído); onde como "" passará na validação e não lançará o erro.

Esse problema pode ser resolvido no EF 7+. Referência: - https://github.com/aspnet/EntityFramework/issues/2610 ).

Edit: [Required (AllowEmptyStrings = true)] resolverá esse problema, permitindo que string.Empty seja validada.

Anthony
fonte
2

Como String.Empty não é uma constante em tempo de compilação, você não pode usá-lo como um valor padrão na definição de função.

public void test(int i=0,string s="")
    {
      // Function Body
    }
tsiva124
fonte
1
Apenas o resumo de esta resposta: public void test(int i=0, string s=string.Empty) {}não irá compilar e dizer "valor do parâmetro padrão para 's' deve ser uma constante em tempo de compilação obras resposta do OP..
bugybunny
1

Quando você digitaliza visualmente o código, "" aparece colorido da maneira que as strings são coloridas. string.Empty se parece com um acesso de membro de classe regular. Durante uma rápida olhada, é mais fácil identificar "" ou intuir o significado.

Identifique as strings (a colorização do estouro de pilha não ajuda exatamente, mas no VS isso é mais óbvio):

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;
K0D4
fonte
2
Você faz uma boa observação, mas o desenvolvedor realmente quis dizer ""? Talvez eles pretendessem colocar algum valor ainda desconhecido e esquecerem de retornar? string.Empty tem a vantagem de lhe dar confiança de que o autor original realmente quis dizer string.Empty. É um ponto secundário, eu sei.
13133 mark_h
Além disso, um desenvolvedor mal-intencionado (ou descuidado) pode colocar um caractere de largura zero entre as aspas, em vez de usar a sequência de escape apropriada, como \u00ad.
Palec 18/05/19
-8

Todo mundo aqui deu um bom esclarecimento teórico. Eu tinha uma dúvida semelhante. Então, eu tentei uma codificação básica nele. E eu achei a diferença. Aqui está a diferença.

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


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

Parece que "Nulo" significa absolutamente nulo e "String.Empty" significa que contém algum tipo de valor, mas está vazio.

DeepakTheGeek
fonte
7
Observe que a pergunta é sobre ""versus string.Empty. Somente ao tentar dizer se a string está vazia, nullfoi mencionado.
Palec