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

Comparar cadena 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 uno debe usarse sobre los demás. ¿Se debe evitar a toda costa? ¿Hay más que no he enumerado?

string testString = "Test"; string anotherString = "Another"; if (testString.CompareTo(anotherString) == 0) {} if (testString.Equals(anotherString)) {} if (testString == anotherString) {} 

(Nota: estoy buscando la igualdad en este ejemplo, no menos que o más que eso, pero no dude en comentar sobre eso también)

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

stringValue.CompareTo(otherStringValue)

  1. null viene antes de una cadena
  2. usa CultureInfo.CurrentCulture.CompareInfo.Compare , lo que significa que utilizará una comparación cultural dependiente. Esto podría significar que ß se comparará igual a SS en Alemania, o similar

stringValue.Equals(otherStringValue)

  1. null no se considera igual a nada
  2. a menos que especifique una opción StringComparison , utilizará 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 método static Equals(string a, string b) (que a su vez va a un EqualsHelper interno para hacer la comparación).
  3. Llamar a .Equals() en una cadena null obtiene una excepción de referencia null , mientras que en == no lo hace.

Object.ReferenceEquals(stringValue, otherStringValue)

Solo comprueba que las referencias sean las mismas, es decir, que no se trata solo de dos cadenas con el mismo contenido, sino que está 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 compararlas.

Mi consejo es que si solo quiere verificar la igualdad, decida si quiere usar una comparación dependiente de la cultura o no, y luego use .Equals o .Equals , dependiendo de la opción.

Desde MSDN:

“El método CompareTo fue diseñado principalmente para su uso en operaciones de clasificación o alfabetización. No debe utilizarse 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 .Equals lugar de .CompareTo cuando buscan únicamente la igualdad. No estoy seguro de si hay una diferencia entre .Equals y == para la clase de string . A veces .Equals o Object.ReferenceEquals lugar de == para mis propias clases en caso de que alguien venga más tarde y redefina el operador == para esa clase.

Si alguna vez sientes curiosidad acerca de las diferencias en los métodos de BCL, Reflector es tu amigo 🙂

Sigo estas pautas:

Concordancia exacta: EDITAR: Anteriormente siempre utilicé == operator en el principio de que dentro de Equals (cadena, cadena) el objeto == operador se usa para comparar las referencias de objeto, pero parece strA.Equals (strB) sigue siendo 1-11% más rápido en general que string.Equals (strA, strB), strA == strB y string.CompareOrdinal (strA, strB). Probé un ciclo con un cronómetro en ambos valores de cadena interna / no interna, con longitudes de cadena iguales / diferentes y tamaños variables (1B a 5MB).

 strA.Equals(strB) 

Partido humano legible (culturas occidentales, insensible a mayúsculas y minúsculas):

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

Combinación legible por humanos (Todas las demás culturas, caso insensible / acento / kana / etc. definido por CultureInfo):

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

Emparejamiento humano legible con reglas personalizadas (Todas las demás culturas):

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

Como dijo Ed , CompareTo se usa para clasificar.

Sin embargo, existe 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 la siguiente arrojará una excepción:

 string a = null; string b = "foo"; bool equal = a.Equals(b); 

Y lo siguiente no:

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

Se pueden encontrar buenas explicaciones y prácticas sobre los 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 StringComparison usan por defecto. Hay varias opciones:

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

Cada uno de los tipos de comparación anteriores tiene como objective diferentes casos de uso:

  • 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
    • Configuración relacionada con la seguridad sensible a mayúsculas y minúsculas
  • OrdinalIgnoreCase
    • Identificadores internos que no distinguen entre mayúsculas y minúsculas
    • Identificadores insensibles a mayúsculas / minúsculas en estándares como XML y HTTP
    • Rutas de archivo (en Microsoft Windows)
    • Claves / valores del registro
    • Variables de entorno
    • Identificadores de recursos (nombres de identificadores, por ejemplo)
    • Configuración relacionada con la seguridad insensible a las mayúsculas
  • InvariantCulture o InvariantCultureIgnoreCase
    • Algunos persistieron datos 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 usuarios

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


Método String.CompareTo (String)

De hecho, es una implementación segura del método IComparable.CompareTo . Interpretación por defecto: 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 usará este método

Método String.Compare

Un miembro estático de la clase de cadena que tiene muchas sobrecargas. Interpretación por defecto: CurrentCulture.

Siempre que sea posible, debe invocar una sobrecarga del método Compare que incluya un parámetro StringComparison.

Método String.Equals

Anulado de la clase Object y sobrecargado para la seguridad del tipo. Interpretación por defecto: Ordinal. Darse cuenta de:

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


Clase StringComparer

También hay otra manera de lidiar con las comparaciones de cadenas, especialmente con el objective 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. Las clases como Hashtable, Dictionary, SortedList y SortedList usan la clase StringComparer para fines de clasificación.

No es que el rendimiento por lo general importe el 99% de las veces que necesitas hacer esto, pero si tuvieras que repetirlo varias veces, te recomendaría que utilizas .Equals o == porque tan pronto como encuentra un personaje eso no concuerda arroja todo como falso, pero si usas el CompareTo tendrás que descubrir qué personaje es menos que el otro, lo que lleva a un tiempo de rendimiento ligeramente peor.

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

En los formularios que enumeró aquí, no hay mucha diferencia entre los dos. CompareTo termina llamando a un método CompareInfo que hace una comparación usando la cultura actual; Equals es llamado por el operador == .

Si considera las sobrecargas, entonces las cosas se ponen diferentes. Compare y == solo puede usar la cultura actual para comparar una cadena. Equals y String.Compare pueden tomar un argumento de enumeración de StringComparison que le permite especificar comparaciones insensibles a la cultura o insensibles a mayúsculas y minúsculas. Solo String.Compare permite especificar CultureInfo y realizar comparaciones usando una cultura que no sea la cultural predeterminada.

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

Una GRAN diferencia a tener en cuenta es .Equals () lanzará 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"); 
  • s1.CompareTo (s2): NO usar si el objective 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): mediante el 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).

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

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

Por cierto, esto evaluará a 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.Equals (b) sería cierto aquí.

y si cambias b a:

 b = "MYSTRING"; 

entonces a.Equals (b) es falso, pero

 a.Equals(b, StringComparison.OrdinalIgnoreCase) 

Sería 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 es sensible a las mayúsculas y minúsculas, creo que hay posibles opciones para que CompareTo ignore el caso y tal, pero no tiene tiempo para mirar ahora. Como otros ya han indicado, esto se haría para clasificar. Comparar la igualdad de esta manera resultaría en una sobrecarga innecesaria.

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