Diferencias en los métodos de comparación de cadenas en C #

261

Comparar cadenas en C # es bastante simple. De hecho, hay varias formas de hacerlo. He enumerado algunos en el bloque a continuación. Lo que me interesa son las diferencias entre ellos y cuándo se debe usar uno sobre los demás. ¿Se debe evitar a toda costa? ¿Hay más que no haya enumerado?

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

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

(Nota: estoy buscando igualdad en este ejemplo, no menor o mayor que, pero siéntase libre de comentar sobre eso también)

Craig
fuente
44
Una trampa es que no puede hacer stringValue.Equals (null) como se supone que se puede llamar a un método en nulo
johnc
1
Referencia de MSDN
Robert Harvey
@RobertHarvey La razón por la que vengo a stackoverflow es para no tener que leer varias páginas para obtener respuestas.
Syaiful Nizam Yahya
@Syaiful: La razón por la que vengo a Stack Overflow es para encontrar respuestas que no están en la documentación.
Robert Harvey

Respuestas:

231

Aquí están las reglas sobre cómo funcionan estas funciones:

stringValue.CompareTo(otherStringValue)

  1. null viene antes de una cuerda
  2. usa CultureInfo.CurrentCulture.CompareInfo.Compare, lo que significa que usará una comparación dependiente de la cultura. Esto podría significar que ßse comparará igual que SSen Alemania, o similar

stringValue.Equals(otherStringValue)

  1. null no se considera igual a nada
  2. a menos que especifique una StringComparisonopción, usará lo que parece una verificación de igualdad ordinal directa, ßes decir, no es lo mismo que SS, en cualquier idioma o cultura

stringValue == otherStringValue

  1. No es lo mismo que stringValue.Equals().
  2. El ==operador llama al Equals(string a, string b)método estático (que a su vez va a un interno EqualsHelperpara hacer la comparación.
  3. Llamar .Equals()a una nullcadena obtiene una nullexcepción de referencia, mientras que en ==no.

Object.ReferenceEquals(stringValue, otherStringValue)

Simplemente verifica que las referencias sean las mismas, es decir, no son solo dos cadenas con el mismo contenido, estás comparando un objeto de cadena consigo mismo.


Tenga en cuenta que con las opciones anteriores que usan llamadas a métodos, hay sobrecargas con más opciones para especificar cómo comparar.

Mi consejo si solo desea verificar la igualdad es decidir si desea usar una comparación culturalmente dependiente o no, y luego usar .CompareToo .Equals, según la elección.

Lasse V. Karlsen
fuente
55
"stringValue.Equals (otherStringValue): nulo no es igual a nulo" Lol, yo diría que no. nulo es igual a la excepción ObjectReferenceNotSet.
Kevin
29
== no es lo mismo que .Equals () ... El operador == llama al método estático Equals (cadena a, cadena b) (que a su vez va a un EqualsHelper interno para hacer la comparación. Llamar a .Equals en un valor nulo la cadena obtiene una referencia nula exc., mientras que en == no.
Dan C.
2
Por otro lado, .Equals es un poco más rápido (una llamada de método menos internamente), pero menos legible, posiblemente, por supuesto :).
Dan C.
Estaba pensando que '==' hará comparaciones de referencia y object.equals hará comparaciones de valor. ¿Cómo '==' y string.equals funcionan igual?
amesh
@ LasseV.Karlsen ¿Cuál es tu opinión String.Compare?
JDandChips
72

De MSDN:

"El método CompareTo fue diseñado principalmente para su uso en operaciones de clasificación o alfabetización. No debe usarse cuando el propósito principal de la llamada al método es determinar si dos cadenas son equivalentes. Para determinar si dos cadenas son equivalentes, llame al método Equals. "

Sugieren usar en .Equalslugar de .CompareTocuando se busca únicamente la igualdad. No estoy seguro de si hay una diferencia entre .Equalsy ==para la stringclase. A veces usaré .Equalso en Object.ReferenceEqualslugar de ==para mis propias clases en caso de que alguien venga más tarde y redefina el ==operador para esa clase.

Ed S.
fuente
18
¿Te ha pasado eso alguna vez? (Redefiniendo ==) ... Lo veo como una programación muuuuuy defensiva =)
juan
Sí, es por eso que ahora uso Object.ReferenceEquals cuando busco igualdad de objetos :). Puede ser un poco excesivamente defensivo, pero no soy maníaco al respecto y, sinceramente, esta situación no aparece con mucha frecuencia.
Ed S.
Dudo que esta 'codificación defensiva' sea útil. ¿Qué sucede si el propietario de la clase necesita anular el operador == y luego descubre que nadie lo está usando?
Dave Van den Eynde
1
@DaveVandenEynde: Sí ... escribí esto hace un tiempo. No hago esto regularmente, solo anulo. Igual cuando es apropiado.
Ed S.
1
La recomendación de Microsoft se registra aquí: Mejores prácticas para usar cadenas en .NET Framework
JJS
50

Si alguna vez tiene curiosidad sobre las diferencias en los métodos BCL, Reflector es su amigo :-)

Sigo estas pautas:

Concordancia exacta: EDITAR: Anteriormente siempre usé == operador en el principio de que dentro de Igual (cadena, cadena) el objeto == operador se usa para comparar las referencias de objeto, pero parece que strA.Equals (strB) todavía es 1-11% más rápido en general que string.Equals (strA, strB), strA == strB y string.CompareOrdinal (strA, strB). Probé en bucle con un StopWatch en valores de cadena internados / no internados, con longitudes de cadena iguales / diferentes y tamaños variables (1B a 5MB).

strA.Equals(strB)

Combinación legible por humanos (culturas occidentales, sin distinción entre mayúsculas y minúsculas):

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

Concordancia legible por humanos (todas las demás culturas, mayúsculas / minúsculas insensibles / kana / etc definidas por CultureInfo):

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

Combinación legible por humanos con reglas personalizadas (todas las demás culturas):

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

Como dijo Ed , CompareTo se usa para ordenar.

Sin embargo, hay una diferencia entre .Equals y ==.

== resuelve esencialmente el siguiente 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);

La razón simple es que lo siguiente arrojará una excepción:

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

bool equal = a.Equals(b);

Y lo siguiente no lo hará:

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

bool equal = a == b;
Jonathan C Dickinson
fuente
15

Se pueden encontrar buenas explicaciones y prácticas sobre problemas de comparación de cadenas en el artículo Nuevas recomendaciones para usar cadenas en Microsoft .NET 2.0 y también en Mejores prácticas para usar cadenas en .NET Framework .


Cada uno de los métodos mencionados (y otros) tiene un propósito particular. La diferencia clave entre ellos es qué tipo de Enumeración de comparación de cadenas están utilizando de forma predeterminada. Hay varias opciones:

  • Cultura actual
  • CurrentCultureIgnoreCase
  • Cultura invariable
  • InvariantCultureIgnoreCase
  • Ordinal
  • OrdinalIgnoreCase

Cada uno de los tipos de comparación anteriores apunta a un caso de uso diferente:

  • Ordinal
    • Identificadores internos sensibles a mayúsculas y minúsculas
    • Identificadores sensibles a mayúsculas y minúsculas en estándares como XML y HTTP
    • Configuraciones sensibles a mayúsculas y minúsculas relacionadas con la seguridad
  • OrdinalIgnoreCase
    • Identificadores internos que no distinguen entre mayúsculas y minúsculas
    • Identificadores que no distinguen entre mayúsculas y minúsculas en estándares como XML y HTTP
    • Rutas de archivo (en Microsoft Windows)
    • Claves de registro / valores
    • Variables de entorno
    • Identificadores de recursos (nombres de identificador, por ejemplo)
    • Configuración de seguridad insensible a mayúsculas y minúsculas
  • InvariantCulture o InvariantCultureIgnoreCase
    • Algunos datos persistentes lingüísticamente relevantes
    • Visualización de datos lingüísticos que requieren un orden de clasificación fijo
  • CurrentCulture o CurrentCultureIgnoreCase
    • Datos mostrados al usuario
    • La mayoría de los aportes del usuario

Tenga en cuenta que la enumeración de comparación de cadenas, así como las sobrecargas para los métodos de comparación de cadenas, existe desde .NET 2.0.


Método String.CompareTo (String)

De hecho, es una implementación segura del método IComparable.CompareTo . Interpretación predeterminada: CurrentCulture.

Uso:

El método CompareTo fue diseñado principalmente para su uso en operaciones de clasificación o alfabetización

Así

La implementación de la interfaz IComparable necesariamente utilizará este método

Método de comparación de cadenas

Un miembro estático de String Class que tiene muchas sobrecargas. Interpretación predeterminada: CurrentCulture.

Siempre que sea posible, debe llamar a una sobrecarga del método Compare que incluye un parámetro StringComparison.

Método String.Equals

Anulado de la clase Object y sobrecargado por seguridad de tipo. Interpretación predeterminada: ordinal. Darse cuenta de:

Los métodos de igualdad de la clase String incluyen los Equals estáticos , el operador estático == y el método de instancia Equals .


Clase StringComparer

También hay otra forma de lidiar con las comparaciones de cadenas, especialmente con el objetivo de ordenar:

Puede usar la clase StringComparer para crear una comparación específica de tipo para ordenar los elementos en una colección genérica. Clases como Hashtable, Dictionary, SortedList y SortedList utilizan la clase StringComparer para fines de clasificación.

Ryszard Dżegan
fuente
2
Según algunas otras publicaciones en SO, todos los métodos, excepto los ordinales, tienen casos en los que Comparar (a, b) y Comparar (b, a) pueden devolver 1, y el error se ha clasificado como "no se solucionará ". Como tal, no estoy seguro de que tales comparaciones tengan algún caso de uso.
supercat
@supercat, ¿puedes vincular a eso o dar un ejemplo?
Noctis
1
Consulte stackoverflow.com/questions/17599084/… para una discusión del problema.
supercat
7

No es que el rendimiento generalmente importe con el 99% de las veces que necesitas hacer esto, pero si tuvieras que hacerlo en un bucle varios millones de veces, te sugiero que uses .Equals o == porque tan pronto como encuentre un personaje eso no coincide, arroja todo como falso, pero si usa CompareTo, tendrá que descubrir qué personaje es menor que el otro, lo que lleva a un tiempo de rendimiento ligeramente peor.

Si su aplicación se ejecutará en diferentes países, le recomiendo que eche un vistazo a las implicaciones de CultureInfo y posiblemente use .Equals. Como solo escribo aplicaciones para EE. UU. (Y no me importa si alguien no funciona correctamente), siempre uso ==.

viggity
fuente
5

En los formularios que enumeró aquí, no hay mucha diferencia entre los dos. CompareTotermina llamando a un CompareInfométodo que hace una comparación usando la cultura actual; Equalses llamado por el ==operador.

Si considera sobrecargas, entonces las cosas se ponen diferentes. Comparey ==solo puede usar la cultura actual para comparar una cadena. Equalsy String.Comparepuede tomar un StringComparisonargumento de enumeración que le permite especificar comparaciones insensibles a la cultura o mayúsculas y minúsculas. Solo le String.Comparepermite especificar ay CultureInforealizar comparaciones utilizando una cultura distinta de la cultura predeterminada.

Debido a su versatilidad, encuentro que uso String.Comparemás que cualquier otro método de comparación; me permite especificar exactamente lo que quiero.

OwenP
fuente
2

Una gran diferencia a tener en cuenta es .Equals () arrojará una excepción si la primera cadena es nula, mientras que == no lo hará.

       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");
Rauld
fuente
0
  • s1.CompareTo (s2): NO lo use si el propósito principal es determinar si dos cadenas son equivalentes
  • s1 == s2: no se puede ignorar el caso
  • s1.Equals (s2, StringComparison): Lanza NullReferenceException si s1 es nulo
  • String.Equals (s2, StringComparison): ¡ Por proceso de eliminación, este método estático es el GANADOR (suponiendo un caso de uso típico para determinar si dos cadenas son equivalentes)!
John DiFini
fuente
-1

Usar .Equals también es mucho más fácil de leer .

tostada casera
fuente
-9

con .Equals, también obtienes las opciones de StringComparison. Muy útil para ignorar el caso y otras cosas.

por cierto, esto se evaluará como falso

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

return a==b

Como == compara los valores de a y b (que son punteros), esto solo se evaluará como verdadero si los punteros apuntan al mismo objeto en la memoria. .Equals desreferencia los punteros y compara los valores almacenados en los punteros. a. Igual (b) sería cierto aquí.

y si cambia b a:

b = "MYSTRING";

entonces a.Equals (b) es falso, pero

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

seria verdad

a.CompareTo (b) llama a la función CompareTo de la cadena que compara los valores en los punteros y devuelve <0 si el valor almacenado en a es menor que el valor almacenado en b, devuelve 0 si a.Equals (b) es verdadero y > 0 de lo contrario. Sin embargo, esto distingue entre mayúsculas y minúsculas, creo que posiblemente haya opciones para que CompareTo ignore mayúsculas y minúsculas, pero no tengo tiempo para mirar ahora. Como ya han dicho otros, esto se haría para clasificar. Comparar la igualdad de esta manera resultaría en una sobrecarga innecesaria.

Estoy seguro de que estoy dejando de lado las cosas, pero creo que esto debería ser suficiente información para comenzar a experimentar si necesita más detalles.

David
fuente
9
La parte a == b es incorrecta. El operador == está efectivamente sobrecargado para la clase String y compara los valores independientemente de las referencias reales.
Goyuix