strings - string.compare c# ejemplo
¿Los operadores string.Equals() y== son realmente iguales? (8)
Esta pregunta ya tiene una respuesta aquí:
- Diferencia C # entre == y Equals () 16 respuestas
¿Son realmente iguales? Hoy, me encontré con este problema. Aquí está el volcado de la ventana inmediata:
?s
"Category"
?tvi.Header
"Category"
?s == tvi.Header
false
?s.Equals(tvi.Header)
true
?s == tvi.Header.ToString()
true
Entonces, tanto s
como tvi.Header
contienen "Category", pero ==
devuelve false y Equals()
devuelve true.
s
se define como una cadena, tvi.Header
es en realidad un TreeViewItem.Header
WPF. Entonces, ¿por qué están dando resultados diferentes? Siempre pensé que eran intercambiables en C #.
¿Alguien puede explicar por qué esto es?
Además de la respuesta de Jon Skeet , me gustaría explicar por qué la mayoría de las veces, al usar ==
, la respuesta es true
en diferentes instancias de cadena con el mismo valor:
string a = "Hell";
string b = "Hello";
a = a + "o";
Console.WriteLine(a == b);
Como puede ver, b
deben ser instancias de cadena diferentes, pero debido a que las cadenas son inmutables, el tiempo de ejecución utiliza el llamado internado de cadena para permitir que a
b
referencia a la misma cadena en la memoria. El operador ==
para objetos verifica la referencia, y dado que tanto a
como b
referencia a la misma instancia, el resultado es true
. Cuando cambias uno de ellos, se crea una nueva instancia de cadena, por lo que la internación de cadena es posible.
Por cierto, la respuesta de Jon Skeet no es completa. De hecho, x == y
es false
pero eso es solo porque está comparando objetos y los objetos se comparan por referencia. Si escribe (string)x == (string)y
, devolverá true
nuevamente. Por lo tanto, las cadenas tienen su == - operador sobrecargado, que llama a String.Equals
debajo.
Aquí hay muchas respuestas descriptivas, así que no voy a repetir lo que ya se ha dicho. Lo que me gustaría agregar es el siguiente código que muestra todas las permutaciones que se me ocurren. El código es bastante largo debido al número de combinaciones. Siéntase libre de colocarlo en MSTest y ver la salida por sí mismo (la salida se incluye en la parte inferior).
Esta evidencia apoya la respuesta de Jon Skeet.
Código:
[TestMethod]
public void StringEqualsMethodVsOperator()
{
string s1 = new StringBuilder("string").ToString();
string s2 = new StringBuilder("string").ToString();
Debug.WriteLine("string a = /"string/";");
Debug.WriteLine("string b = /"string/";");
TryAllStringComparisons(s1, s2);
s1 = null;
s2 = null;
Debug.WriteLine(string.Join(string.Empty, Enumerable.Repeat("-", 20)));
Debug.WriteLine(string.Empty);
Debug.WriteLine("string a = null;");
Debug.WriteLine("string b = null;");
TryAllStringComparisons(s1, s2);
}
private void TryAllStringComparisons(string s1, string s2)
{
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- string.Equals --");
Debug.WriteLine(string.Empty);
Try((a, b) => string.Equals(a, b), s1, s2);
Try((a, b) => string.Equals((object)a, b), s1, s2);
Try((a, b) => string.Equals(a, (object)b), s1, s2);
Try((a, b) => string.Equals((object)a, (object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- object.Equals --");
Debug.WriteLine(string.Empty);
Try((a, b) => object.Equals(a, b), s1, s2);
Try((a, b) => object.Equals((object)a, b), s1, s2);
Try((a, b) => object.Equals(a, (object)b), s1, s2);
Try((a, b) => object.Equals((object)a, (object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- a.Equals(b) --");
Debug.WriteLine(string.Empty);
Try((a, b) => a.Equals(b), s1, s2);
Try((a, b) => a.Equals((object)b), s1, s2);
Try((a, b) => ((object)a).Equals(b), s1, s2);
Try((a, b) => ((object)a).Equals((object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- a == b --");
Debug.WriteLine(string.Empty);
Try((a, b) => a == b, s1, s2);
#pragma warning disable 252
Try((a, b) => (object)a == b, s1, s2);
#pragma warning restore 252
#pragma warning disable 253
Try((a, b) => a == (object)b, s1, s2);
#pragma warning restore 253
Try((a, b) => (object)a == (object)b, s1, s2);
}
public void Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, T1 in1, T2 in2)
{
T3 out1;
Try(tryFunc, e => { }, in1, in2, out out1);
}
public bool Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, Action<Exception> catchFunc, T1 in1, T2 in2, out T3 out1)
{
bool success = true;
out1 = default(T3);
try
{
out1 = tryFunc.Compile()(in1, in2);
Debug.WriteLine("{0}: {1}", tryFunc.Body.ToString(), out1);
}
catch (Exception ex)
{
Debug.WriteLine("{0}: {1} - {2}", tryFunc.Body.ToString(), ex.GetType().ToString(), ex.Message);
success = false;
catchFunc(ex);
}
return success;
}
Salida:
string a = "string";
string b = "string";
-- string.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- object.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- a.Equals(b) --
a.Equals(b): True
a.Equals(Convert(b)): True
Convert(a).Equals(b): True
Convert(a).Equals(Convert(b)): True
-- a == b --
(a == b): True
(Convert(a) == b): False
(a == Convert(b)): False
(Convert(a) == Convert(b)): False
--------------------
string a = null;
string b = null;
-- string.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- object.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- a.Equals(b) --
a.Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
a.Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.
-- a == b --
(a == b): True
(Convert(a) == b): True
(a == Convert(b)): True
(Convert(a) == Convert(b)): True
C # tiene dos conceptos "iguales": Equals
y ReferenceEquals
. Para la mayoría de las clases que encontrará, el operador ==
usa uno u otro (o ambos), y generalmente solo prueba los ReferenceEquals
cuando maneja tipos de referencia (pero la clase de string
es una instancia donde C # ya sabe cómo probar la igualdad de valores) .
-
Equals
compara valores. (Aunque dos variablesint
separadas no existen en el mismo lugar en la memoria, pueden contener el mismo valor). -
ReferenceEquals
compara la referencia y devuelve si los operandos apuntan al mismo objeto en la memoria.
Código de ejemplo:
var s1 = new StringBuilder("str");
var s2 = new StringBuilder("str");
StringBuilder sNull = null;
s1.Equals(s2); // True
object.ReferenceEquals(s1, s2); // False
s1 == s2 // True - it calls Equals within operator overload
s1 == sNull // False
object.ReferenceEquals(s1, sNull); // False
s1.Equals(sNull); // Nono! Explode (Exception)
Dos diferencias:
Equals
es polimórfico (es decir, se puede anular y la implementación utilizada dependerá del tipo de tiempo de ejecución del objeto de destino), mientras que la implementación de==
used se determina en función de los tipos de tiempo de compilación de los objetos:// Avoid getting confused by interning object x = new StringBuilder("hello").ToString(); object y = new StringBuilder("hello").ToString(); if (x.Equals(y)) // Yes // The compiler doesn''t know to call ==(string, string) so it generates // a reference comparision instead if (x == y) // No string xs = (string) x; string ys = (string) y; // Now *this* will call ==(string, string), comparing values appropriately if (xs == ys) // Yes
Equals
se dispararán si lo llamas nulo, == no lo harástring x = null; string y = null; if (x.Equals(y)) // Bang if (x == y) // Yes
Tenga en cuenta que puede evitar que esto último sea un problema al usar object.Equals
:
if (object.Equals(x, y)) // Fine even if x or y is null
Está claro que tvi.header
no es una String
. El ==
es un operador que está sobrecargado por String
clase String
, lo que significa que funcionará solo si el compilador sabe que ambos lados del operador son String
.
La propiedad Header
del TreeViewItem
se escribe estáticamente para ser de tipo object
.
Por lo tanto, el ==
produce false
. Puede reproducir esto con el siguiente fragmento simple:
object s1 = "Hallo";
// don''t use a string literal to avoid interning
string s2 = new string(new char[] { ''H'', ''a'', ''l'', ''l'', ''o'' });
bool equals = s1 == s2; // equals is false
equals = string.Equals(s1, s2); // equals is true
Las contradicciones aparentes que aparecen en la pregunta se deben a que en un caso se llama a la función Equals
en un objeto de string
y en el otro caso al operador ==
se llama en el tipo System.Object
. string
y el object
implementan la igualdad de manera diferente entre sí (valor frente a referencia, respectivamente).
Más allá de este hecho, cualquier tipo puede definir ==
y es Equals
diferente, por lo que en general no son intercambiables.
Aquí hay un ejemplo que usa double
(de la nota de Joseph Albahari a §7.9.2 de la especificación del lenguaje C #):
double x = double.NaN;
Console.WriteLine (x == x); // False
Console.WriteLine (x != x); // True
Console.WriteLine (x.Equals(x)); // True
Continúa diciendo que el double.Equals(double)
fue diseñado para funcionar correctamente con listas y diccionarios. El operador ==
, por otro lado, fue diseñado para seguir el estándar IEEE 754 para los tipos de punto flotante.
En el caso específico de determinar la igualdad de cadenas, la preferencia de la industria es no utilizar ==
ni string.Equals(string)
mayor parte del tiempo. Estos métodos determinan si dos cadenas son del mismo carácter por carácter, que rara vez es el comportamiento correcto. Es mejor usar string.Equals(string, StringComparison)
, que le permite especificar un tipo particular de comparación. Al utilizar la comparación correcta, puede evitar muchos errores potenciales (muy difíciles de diagnosticar).
Aquí hay un ejemplo:
string one = "Caf/u00e9"; // U+00E9 LATIN SMALL LETTER E WITH ACUTE
string two = "Cafe/u0301"; // U+0301 COMBINING ACUTE ACCENT
Console.WriteLine(one == two); // False
Console.WriteLine(one.Equals(two)); // False
Console.WriteLine(one.Equals(two, StringComparison.InvariantCulture)); // True
Ambas cadenas en este ejemplo tienen el mismo aspecto ("Café"), por lo que esto podría ser muy difícil de depurar si se utiliza una igualdad ingenua (ordinal).
Un objeto está definido por un OBJECT_ID, que es único. Si A y B son objetos y A == B es verdadero, entonces son el mismo objeto, tienen los mismos datos y métodos, pero esto también es cierto:
A.OBJECT_ID == B.OBJECT_ID
si A.Equals (B) es verdadero, eso significa que los dos objetos están en el mismo estado, pero esto no significa que A sea lo mismo que B.
Las cuerdas son objetos.
Tenga en cuenta que los operadores == y Igual son reflexivos, simétricos, tranzitivos, por lo que son relaciones equivalénicas (para usar términos algebraicos relacionales)
Lo que esto significa: si A, B y C son objetos, entonces:
(1) A == A es siempre verdadera; A. Las ecuaciones (A) siempre son ciertas (reflexividad)
(2) si A == B entonces B == A; Si A.Equals (B) entonces B.Equals (A) (simetría)
(3) si A == B y B == C, entonces A == C; si A.Equals (B) y B.Equals (C) entonces A.Equals (C) (tranzitividad)
Además, puedes notar que esto también es cierto:
(A == B) => (A.Equals (B)), pero lo inverso no es cierto.
A B =>
0 0 1
0 1 1
1 0 0
1 1 1
Ejemplo de la vida real: dos hamburguesas del mismo tipo tienen las mismas propiedades: son objetos de la clase Hamburger, sus propiedades son exactamente iguales, pero son entidades diferentes. Si compras estas dos hamburguesas y comes una, la otra no se comerá. Entonces, la diferencia entre Igual y ==: Tienes hamburguesa1 y hamburguesa2. Están exactamente en el mismo estado (el mismo peso, la misma temperatura, el mismo sabor), por lo que hamburger1.Equals (hamburger2) es cierto. Pero hamburger1 == hamburger2 es falso, porque si el estado de hamburger1 cambia, el estado de hamburger2 no necesariamente cambia y viceversa.
Si usted y un amigo obtienen una hamburguesa, que es la suya y la suya al mismo tiempo, entonces debe decidir dividir la hamburguesa en dos partes, porque usted.getHamburger () == friend.getHamburger () es verdad y si esto sucede : friend.eatHamburger (), entonces tu Hamburguesa también se comerá.
Podría escribir otros matices sobre Equals y ==, pero tengo hambre, así que tengo que irme.
Saludos cordiales, Lajos Arpad.