Diferenças na string comparam métodos em C #


261

Comparar string em C # é bastante simples. De fato, existem várias maneiras de fazer isso. Eu listei alguns no bloco abaixo. O que estou curioso são as diferenças entre eles e quando um deve ser usado em relação aos outros? Deve-se evitar a todo custo? Há mais que eu não listei?

string testString = "Test";
string anotherString = "Another";

if (testString.CompareTo(anotherString) == 0) {}
if (testString.Equals(anotherString)) {}
if (testString == anotherString) {}

(Observação: estou procurando a igualdade neste exemplo, não menos que ou maior que, mas fique à vontade para comentar sobre isso também)


4
Uma armadilha é que você não pode fazer stringValue.Equals (null) como que assume que você pode chamar um método em nulo
johnc


@RobertHarvey A razão pela qual eu vim para o stackoverflow é que não preciso ler várias páginas para obter respostas.
Syaiful Nizam Yahya 12/12/19

@ Syaiful: A razão pela qual venho ao Stack Overflow é encontrar respostas que não estão na documentação.
Robert Harvey

Respostas:


231

Aqui estão as regras de como essas funções funcionam:

stringValue.CompareTo(otherStringValue)

  1. null vem antes de uma string
  2. usa CultureInfo.CurrentCulture.CompareInfo.Compare, o que significa que usará uma comparação dependente da cultura. Isso pode significar que ßserá comparado com igual SSna Alemanha ou similar

stringValue.Equals(otherStringValue)

  1. null não é considerado igual a nada
  2. a menos que você especifique uma StringComparisonopção, ela usará o que parece uma verificação direta de igualdade ordinal, ou ßseja, não é a mesma que SSem qualquer idioma ou cultura

stringValue == otherStringValue

  1. Não é o mesmo que stringValue.Equals().
  2. O ==operador chama o Equals(string a, string b)método estático (que por sua vez vai para um interno EqualsHelperpara fazer a comparação.
  3. A chamada .Equals()de uma nullsequência obtém uma nullexceção de referência, enquanto a ==ativação não.

Object.ReferenceEquals(stringValue, otherStringValue)

Apenas verifica se as referências são iguais, ou seja, não são apenas duas strings com o mesmo conteúdo, você está comparando um objeto string com ele mesmo.


Observe que, com as opções acima que usam chamadas de método, há sobrecargas com mais opções para especificar como comparar.

Meu conselho, se você apenas deseja verificar a igualdade, decide se deseja usar uma comparação dependente da cultura ou não e, em seguida, use .CompareToou .Equals, dependendo da escolha.


5
"stringValue.Equals (otherStringValue): null não é igual a null" Lol, eu diria que não. null é igual a exceção ObjectReferenceNotSet.
Kevin

29
== não é o mesmo que .Equals () ... O operador == chama o método estático Equals (string a, string b) (que por sua vez vai para um EqualsHelper interno para fazer a comparação. String obtém exc referência nula, enquanto em == não..
Dan C.

2
Por outro lado, .Equals é um pouco mais rápido (menos uma chamada de método internamente), mas menos legível - sem dúvida, é claro :).
Dan C.

Eu estava pensando que '==' fará comparações de referência e object.equals fará comparações de valor.Como '==' e string.equals funcionam da mesma maneira?
Amesh

@ LasseV.Karlsen Qual é a sua opinião String.Compare?
JDandChips 10/10/12

72

Do MSDN:

"O método CompareTo foi projetado principalmente para uso em operações de classificação ou alfabetização. Não deve ser usado quando o objetivo principal da chamada de método é determinar se duas seqüências de caracteres são equivalentes. Para determinar se duas seqüências de caracteres são equivalentes, chame o método Equals. "

Eles sugerem o uso em .Equalsvez de .CompareToprocurar apenas a igualdade. Não tenho certeza se existe uma diferença entre .Equalse ==para a stringturma. Às vezes, usarei .Equalsou em Object.ReferenceEqualsvez de ==para minhas próprias classes, caso alguém apareça mais tarde e redefina o ==operador para essa classe.


18
Isso já aconteceu com você? (Redefining ==) ... Eu vejo isso como waaaay programação muito defensiva =)
juan

Sim, é por isso que agora uso Object.ReferenceEquals quando procuro igualdade de objetos :). Pode ser um pouco defensivo demais, mas não sou maníaco por isso e, sinceramente, essa situação não aparece com muita frequência.
224 Ed Ed S.

Duvido que essa 'codificação defensiva' seja útil. E se o proprietário da classe precisar substituir o operador == e descobrir que ninguém está usando?
Dave Van den Eynde

1
@DaveVandenEynde: Sim ... eu escrevi isso há um tempo. Eu não faço isso regularmente, apenas substituindo .Equals quando apropriado.
Ed S.


50

Se você estiver curioso sobre as diferenças nos métodos BCL, o Reflector é seu amigo :-)

Eu sigo estas diretrizes:

Correspondência exata: EDIT: Eu sempre usei o operador == com o princípio de que dentro de Equals (string, string) o operador object == é usado para comparar as referências do objeto, mas parece que strA.Equals (strB) ainda é de 1 a 11% geral mais rápido que string.Equals (strA, strB), strA == strB e string.CompareOrdinal (strA, strB). Fiz um loop testado com um StopWatch em valores de sequência internos / não internos, com comprimentos de sequência iguais / diferentes e tamanhos variados (1B a 5MB).

strA.Equals(strB)

Correspondência legível por humanos (culturas ocidentais, sem distinção entre maiúsculas e minúsculas):

string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0

Correspondência legível por humanos (todas as outras culturas, maiúsculas / minúsculas / sotaque / kana / etc definidas por CultureInfo):

string.Compare(strA, strB, myCultureInfo) == 0

Correspondência legível por humanos com regras personalizadas (todas as outras culturas):

CompareOptions compareOptions = CompareOptions.IgnoreCase
                              | CompareOptions.IgnoreWidth
                              | CompareOptions.IgnoreNonSpace;
string.Compare(strA, strB, CultureInfo.CurrentCulture, compareOptions) == 0

18

Como Ed disse, o CompareTo é usado para classificação.

Há uma diferença, no entanto, entre .Equals e ==.

== resolve essencialmente o seguinte código:

if(object.ReferenceEquals(left, null) && 
   object.ReferenceEquals(right, null))
    return true;
if(object.ReferenceEquals(left, null))
    return right.Equals(left);
return left.Equals(right);

O motivo simples é o seguinte lançará uma exceção:

string a = null;
string b = "foo";

bool equal = a.Equals(b);

E o seguinte não será:

string a = null;
string b = "foo";

bool equal = a == b;

15

Boas explicações e práticas sobre problemas de comparação de cadeias podem ser encontradas no artigo Novas recomendações para usar cadeias de caracteres no Microsoft .NET 2.0 e também em Práticas recomendadas para usar cadeias de caracteres no .NET Framework .


Cada um dos métodos mencionados (e outros) tem uma finalidade específica. A principal diferença entre eles é que tipo de enumeração StringComparison eles estão usando por padrão. Existem várias opções:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • Ordinal
  • OrdinalIgnoreCase

Cada um dos tipos de comparação acima visa diferentes casos de uso:

  • Ordinal
    • Identificadores internos que diferenciam maiúsculas de minúsculas
    • Identificadores que diferenciam maiúsculas de minúsculas em padrões como XML e HTTP
    • Configurações relacionadas à segurança que diferenciam maiúsculas de minúsculas
  • OrdinalIgnoreCase
    • Identificadores internos que não diferenciam maiúsculas de minúsculas
    • Identificadores que não diferenciam maiúsculas de minúsculas em padrões como XML e HTTP
    • Caminhos de arquivo (no Microsoft Windows)
    • Chaves / valores do registro
    • Variáveis ​​ambientais
    • Identificadores de recursos (manipular nomes, por exemplo)
    • Configurações relacionadas à segurança sem distinção entre maiúsculas e minúsculas
  • InvariantCulture ou InvariantCultureIgnoreCase
    • Alguns dados persistentes de relevância linguística
    • Exibição de dados linguísticos que exigem uma ordem de classificação fixa
  • CurrentCulture ou CurrentCultureIgnoreCase
    • Dados exibidos para o usuário
    • A maioria das entradas do usuário

Observe que a enumeração StringComparison , bem como sobrecargas para métodos de comparação de strings, existem desde o .NET 2.0.


Método String.CompareTo (String)

De fato, é uma implementação segura do tipo IComparable.CompareTo Method . Interpretação padrão: CurrentCulture.

Uso:

O método CompareTo foi projetado principalmente para uso em operações de classificação ou alfabetização

portanto

A implementação da interface IComparable necessariamente usará esse método

Método String.Compare

Um membro estático da classe String que possui muitas sobrecargas. Interpretação padrão: CurrentCulture.

Sempre que possível, você deve chamar uma sobrecarga do método Compare que inclui um parâmetro StringComparison.

Método String.Equals

Substituído da classe Object e sobrecarregado para segurança do tipo. Interpretação padrão: Ordinal. Notar que:

Os métodos de igualdade da classe String incluem o Equals estático , o operador estático == e o método de instância Equals .


Classe StringComparer

Há também outra maneira de lidar com comparações de strings, especialmente com a classificação:

Você pode usar a classe StringComparer para criar uma comparação específica de tipo para classificar os elementos em uma coleção genérica. Classes como Hashtable, Dictionary, SortedList e SortedList usam a classe StringComparer para fins de classificação.


2
De acordo com algumas outras postagens no SO, todos os métodos, exceto os ordinais, têm casos em que Compare (a, b) e Compare (b, a) podem retornar 1, e o bug foi classificado como "não será corrigido " Como tal, não tenho certeza se essas comparações têm algum caso de uso.
Supercat 3/14

@ supercat você pode criar um link para isso ou dar um exemplo?
Noctis

1
Consulte stackoverflow.com/questions/17599084/… para obter uma discussão sobre o problema.
Supercat

7

Não que o desempenho geralmente importe com 99% das vezes que você precisa fazer isso, mas se você tivesse que fazer isso várias vezes várias vezes, sugiro que você use .Equals ou == porque assim que encontrar um caractere isso não corresponde, lança a coisa toda como falsa, mas se você usar o CompareTo, ele terá que descobrir qual caractere é menor que o outro, levando a um tempo de desempenho um pouco pior.

Se seu aplicativo estiver em execução em diferentes países, recomendamos que você dê uma olhada nas implicações do CultureInfo e, possivelmente, use .Equals. Como eu realmente só escrevo aplicativos para os EUA (e não me importo se ele não funcionar corretamente por alguém), eu sempre uso ==.


5

Nos formulários listados aqui, não há muita diferença entre os dois. CompareToacaba chamando um CompareInfométodo que faz uma comparação usando a cultura atual; Equalsé chamado pelo ==operador.

Se você considerar sobrecargas, as coisas ficam diferentes. Comparee ==só pode usar a cultura atual para comparar uma sequência. EqualseString.Compare pode usar um StringComparisonargumento de enumeração que permite especificar comparações sem distinção de cultura ou sem distinção entre maiúsculas e minúsculas. String.ComparePermite apenas especificar CultureInfoe realizar comparações usando uma cultura diferente da cultura padrão.

Devido à sua versatilidade, acho que uso String.Comparemais do que qualquer outro método de comparação; permite especificar exatamente o que eu quero.


2

Uma grande diferença a ser observada é .Equals () lançará uma exceção se a primeira string for nula, enquanto que == não.

       string s = null;
        string a = "a";
        //Throws {"Object reference not set to an instance of an object."}
        if (s.Equals(a))
            Console.WriteLine("s is equal to a");
        //no Exception
        if(s==a)
            Console.WriteLine("s is equal to a");

0
  • s1.CompareTo (s2): NÃO use se o objetivo principal for determinar se duas seqüências são equivalentes
  • s1 == s2: não é possível ignorar maiúsculas e minúsculas
  • s1.Equals (s2, StringComparison): lança NullReferenceException se s1 for nulo
  • String.Equals (s2, StringComparison): Por processo de eliminação, esse método estático é o WINNER (assumindo um caso de uso típico para determinar se duas seqüências são equivalentes)!


-9

com .Equals, você também obtém as opções StringComparison. muito útil para ignorar caso e outras coisas.

btw, isso será avaliado como falso

string a = "myString";
string b = "myString";

return a==b

Como == compara os valores de aeb (que são ponteiros), isso só será avaliado como verdadeiro se os ponteiros apontarem para o mesmo objeto na memória. .Equals desreferencia os ponteiros e compara os valores armazenados nos ponteiros. a.Equals (b) seria verdade aqui.

e se você alterar b para:

b = "MYSTRING";

então a.Equals (b) é falso, mas

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

seria verdade

a.CompareTo (b) chama a função CompareTo da string que compara os valores nos ponteiros e retorna <0 se o valor armazenado em a for menor que o valor armazenado em b, retorna 0 se a.Equals (b) for verdadeiro e > 0 caso contrário. No entanto, isso faz distinção entre maiúsculas e minúsculas, acho que existem opções para o CompareTo ignorar maiúsculas e minúsculas, mas não temos tempo para procurar agora. Como outros já declararam, isso seria feito para classificação. Comparar a igualdade dessa maneira resultaria em custos indiretos desnecessários.

Tenho certeza de que estou deixando as coisas de fora, mas acho que essas informações devem ser suficientes para começar a experimentar se você precisar de mais detalhes.


9
A parte a == b está incorreta. O operador == é efetivamente sobrecarregado para a classe String e compara os valores independentemente das referências reais.
Goyuix
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.