2009-05-02 97 views
403

tengo una condición en una aplicación de Silverlight que compara 2 cadenas, por alguna razón, cuando uso == Devuelve falsa mientras .Equals() vuelve cierto.C# diferencia entre == y equals()

Aquí está el código:

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack")) 
{ 
    // Execute code 
} 

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack") 
{ 
    // Execute code 
} 

Cualquier razón de por qué está sucediendo esto?

+2

Vea también: http://stackoverflow.com/questions/144530/or-equals – Arrow

+2

@ColonelPanic Silverlight se clasifica como "WPF en todas partes", WPF está construido sobre .NET y la pregunta usa C#, que es a. Lenguaje NET. –

+4

La cadena anula '==', pero los operadores no son polimórficos. En este código, el operador '==' se invoca en el tipo 'object', que hace una comparación de identidad en lugar de un valor uno. –

Respuesta

339

Cuando == se usa en una expresión de tipo object, se resolverá en System.Object.ReferenceEquals.

Equals es sólo un método virtual y se comporta como tal, por lo que la versión anulado se utilizará (que, por string tipo compara el contenido).

+41

A menos que el operador se implemente específicamente en la clase –

+15

@DominicCronin Esto no es cierto.Incluso si == se implementa en la clase, se ignorará porque el tipo a la izquierda de la comparación es el objeto. Parece que las sobrecargas del operador se determinan en tiempo de compilación y en tiempo de compilación, todo lo que sabe es que el lado izquierdo es un objeto. – MikeKulls

+0

@MikeKulls En la pregunta, el tipo devuelto por .Content es objeto, entonces sí. Sin embargo, Mehrdad dice "un tipo de objeto", no "una referencia de tipo objeto". Mi interpretación fue que se refería a un tipo de referencia, y que la implementación de == se resolvería con respecto a ese objeto. Aun así, las sobrecargas del operador se resuelven de manera similar a las de los métodos virtuales. –

218

Al comparar una referencia de objeto a una cadena (incluso si la referencia del objeto hace referencia a una cadena), se ignora el comportamiento especial del operador == específico de la clase de cadena.

Normalmente (cuando no se trata de cadenas, que es), Equals compara valores, mientras == compara referencias a objetos. Si dos objetos que está comparando se refieren a la misma instancia exacta de un objeto, ambos serán verdaderos, pero si uno tiene el mismo contenido y proviene de una fuente diferente (es una instancia separada con los mismos datos), solo equivale volverá verdadero. Sin embargo, como se señala en los comentarios, la cadena es un caso especial porque anula al operador ==, de modo que cuando se trata exclusivamente de referencias de cadenas (y no de referencias a objetos), solo se comparan los valores incluso si son instancias separadas. El código siguiente ilustra las sutiles diferencias en los comportamientos:

string s1 = "test"; 
string s2 = "test"; 
string s3 = "test1".Substring(0, 4); 
object s4 = s3; 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2)); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3)); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4)); 

la salida es:

True True True 
False True True 
False False True 
+6

Spot on. El operador '==' compara referencias de objeto (comparación superficial) mientras que .Equals() compara contenido de objeto (comparación profunda). Como dijo @mehrdad, .Equals() se reemplaza para proporcionar esa comparación de contenido profundo. – Andrew

+1

Dejaré la publicación aquí porque creo que es valioso enfatizar lo que * no * está sucediendo, ya que debes prestar mucha atención para darte cuenta. (Y creo que vale la pena también el código para demostrar los entendimientos correctos e incorrectos.) Espero que la calificación no sea inferior a 0. – BlueMonkMN

+4

Surely String implementa un operador == personalizado. Si no fuera así, usar == no compararía el contenido. Así que String es un mal ejemplo para usar aquí, ya que no nos ayuda a entender el caso general donde no se ha definido un operador personalizado. –

40

== y .Equals son ambos depende de la conducta se define en el tipo real y el tipo real en el sitio de llamada . Ambos son solo métodos/operadores que pueden ser anulados en cualquier tipo y dado cualquier comportamiento que el autor así lo desee. En mi experiencia, me parece que es común que las personas implementen .Equals en un objeto pero no implementen el operador ==. Esto significa que .Equals realmente medirá la igualdad de los valores, mientras que == medirá si son la misma referencia o no.

Cuando estoy trabajando con un nuevo tipo cuya definición es en flujo o escribir algoritmos genéricos, creo que la mejor práctica es la siguiente

  • Si quiero comparar referencias en C#, yo uso Object.ReferenceEquals directamente (no es necesario en el caso genérico)
  • Si quiero comparar los valores que utilizo EqualityComparer<T>.Default

en algunos casos, cuando siento el uso de == es ambigua voy a utilizar de forma explícita Object.Reference es igual en el código para eliminar la ambigüedad.

Eric Lippert recientemente hizo una publicación de blog sobre el tema de por qué hay 2 métodos de igualdad en el CLR. Vale la pena leer el

+0

Bueno, Jared, violas directamente el famoso "El mejor código aquí no es código". ¿Está esto realmente justificado? Por otro lado, puedo ver de dónde viene esto y por qué podría ser deseable hacer explícita la semántica. Para este caso, prefiero mucho la forma en que VB trata la igualdad de objetos. Es corto * y * inequívoco. –

+0

@Konrad, realmente debería haber dicho "cuando no estoy familiarizado con un tipo, la mejor práctica es la siguiente". Sí, VB tiene una semántica mucho mejor porque realmente separa el valor y la igualdad de referencia. C# mezcla los dos juntos y ocasionalmente causa errores de ambigüedad. – JaredPar

+7

Esto no es del todo cierto. == no puede ser anulado, es un método estático. Solo se puede sobrecargar, lo cual es una diferencia importante. Entonces, el código que se ejecuta para un operador == está vinculado en tiempo de compilación, mientras que Equals es virtual y se encuentra en el tiempo de ejecución. –

2

Estoy un poco confundido aquí. Si el tipo de contenido de tiempo de ejecución es de tipo cadena, ambos == e Igual deben devolver verdadero. Sin embargo, dado que este no parece ser el caso, entonces el tipo de contenido en tiempo de ejecución no es una cadena y llamar a Igual en él está haciendo una igualdad referencial y esto explica por qué Equals ("Energy Attack") falla. Sin embargo, en el segundo caso, la decisión sobre a qué operador sobrecargado == static se debe llamar se realiza en tiempo de compilación y esta decisión parece ser == (string, string). esto me sugiere que el Contenido proporciona una conversión implícita a la cadena.

+2

Lo tienes de vuelta al frente. Para empezar, Equals ("Energy Attack") no falla, == es el que devuelve false. El == falla porque usa el == del objeto, no la cadena. – MikeKulls

+0

De forma predeterminada, el operador == prueba la igualdad de referencia al determinar si dos referencias indican el mismo objeto. Por lo tanto, los tipos de referencia no tienen que implementar operator == para obtener esta funcionalidad. Cuando un tipo es inmutable, es decir, los datos contenidos en la instancia no pueden modificarse, la sobrecarga del operador == para comparar la igualdad de valores en lugar de la igualdad de referencia puede ser útil porque, como objetos inmutables, pueden considerarse iguales que largos ya que tienen el mismo valor. No es una buena idea anular operador == en tipos no inmutables. –

11

Yo agregaría que si lanzas tu objeto a una cadena, entonces funcionará correctamente. Esta es la razón por la cual el compilador le dará una advertencia diciendo "Posible comparación de referencia no intencionada; para obtener una comparación de valor, envíe el lado izquierdo para escribir 'cadena'"

+0

+1 porque esto me ayudó a entender de qué se trata toda la discusión. –

+1

Exactamente. @DominicCronin: observe siempre las advertencias en tiempo de compilación. Si tiene 'object expr = XXX; if (expr == "Energy") {...} ', como el lado izquierdo es de tipo de tiempo de compilación' object', el compilador tiene que usar la sobrecarga 'operator == (object, object)' . Comprueba la igualdad de referencia. Si eso dará 'true' o' false' puede ser difícil de predecir debido a [string interning] (http://en.wikipedia.org/wiki/String_interning). Si sabes _el lado izquierdo es 'nulo' o de tipo' cadena', echa el lado izquierdo a 'cadena' antes de usar' == '. –

+0

para poner parte de eso de otra manera. == (para determinar si usa igualdad de referencia o igualdad de valores) depende del tipo de tiempo de compilación/tipo estático/tipo de lado izquierdo. (ese es el tipo que se resuelve en un análisis de tiempo de compilación). En lugar del tipo de tiempo de ejecución/tipo dinámico/tipo RHS. El código de BlueMonkMN muestra eso, aunque no con el casting. – barlop

1

Agregando un punto más a la respuesta.

.EqualsTo() método le ofrece la posibilidad de comparar con la cultura y las mayúsculas y minúsculas.

8

== operador 1. Si los operandos son Value Types y sus valores son iguales, devuelve true y false. 2. Si los operandos son Reference Types con excepción del string y ambos se refieren al mismo objeto, devuelve true else false. 3. Si los operandos son de tipo serie y sus valores son iguales, devuelve true else false.

.equals 1. Si los operandos son los tipos de referencia, se realiza Reference Equality es decir, si ambos se refieren al mismo objeto, devuelve true y false. 2. Si los operandos son tipos de valor, a diferencia del operador ==, primero verifica su tipo y si sus tipos son iguales, realiza == operator else devuelve falso.

+2

Esto no es correcto. El operador '==' puede estar sobrecargado para cualquier tipo, no solo para cadena. La descripción de una excepción de caso especial solo para una cadena tergiversa la semántica del operador. Sería más exacto, aunque quizás no terriblemente útil, decir "si los operandos son tipos de referencia devuelve verdadero si los operandos se refieren al mismo objeto, a menos que exista una sobrecarga aplicable, en cuyo caso la implementación de esa sobrecarga determina el resultado ". Lo mismo es cierto para 'Equals' con la complicación adicional de que es un método virtual, por lo que su comportamiento puede ser anulado y sobrecargado. – phoog

13

En primer lugar, hay es una diferencia. Para los números de

> 2 == 2.0 
True 

> 2.Equals(2.0) 
False 

Y para cuerdas

> string x = null; 
> x == null 
True 

> x.Equals(null) 
NullReferenceException 

En ambos casos, == se comporta más útil que .Equals

+0

No estoy seguro de que considere la coerción de tipos integrales a tipos de punto flotante con el operador '==' como algo bueno. Por ejemplo, ¿16777216.0f debe ser igual (int) 16777217, (doble) 16777217.0, ambos, o ninguno?Las comparaciones entre los tipos integrales son buenas, pero las comparaciones de coma flotante solo deben realizarse en mi humilde opinión con valores que se emiten explícitamente a los tipos de concordancia. La comparación de un 'float' con otro que no sea' float', o 'double' con algo que no sea' double', me parece un olor de código importante que no debería compilarse sin diagnósticos. – supercat

+1

@supercat Estoy de acuerdo, es penoso que 'x == y' no implique' x/3 == y/3' (intente 'x = 5' y' y = 5.0'). –

+0

Considero que el uso de '/' para la división entera es un defecto en el diseño de C# y Java. "Div" de Pascal e incluso '\' de VB.NET son mucho mejores. Los problemas con '==' son peores, sin embargo: 'x == y' y' y == z' no implica que 'x == z' (considere los tres números en mi comentario anterior). En cuanto a la relación que sugieres, incluso si 'x' y' y' son ambos 'float' o ambos' double', 'x.equals ((Object) y)' no implica que '1.0f/x ==' 1.0f/y' (si tuviera mi druthers, lo garantizaría, incluso si '==' no distingue entre positivo y cero, 'Igual' debería). – supercat

0

El token == en C# se utiliza para dos operadores de igualdad de verificación diferentes. Cuando el compilador encuentra ese token, verificará si alguno de los tipos que se comparan ha implementado una sobrecarga del operador de igualdad para los tipos de combinación específicos que se comparan (*) o para una combinación de tipos a los que se pueden convertir ambos tipos. Si el compilador encuentra tal sobrecarga, la usará. De lo contrario, si los dos tipos son ambos tipos de referencia y no son clases no relacionadas (ya sea una interfaz o pueden ser clases relacionadas), el compilador considerará == como un operador de comparación de referencia. Si no se cumple ninguna condición, la compilación fallará.

Tenga en cuenta que algunos otros idiomas usan tokens separados para los dos operadores de verificación de igualdad. En VB.NET, por ejemplo, el token = se usa dentro de las expresiones exclusivas para el operador de verificación de igualdad descargable, y Is se utiliza como operador de prueba de referencia o prueba nula. An para usar = en un tipo que no anula el operador de comprobación de igualdad fallará, como se intentará utilizar Is para cualquier propósito que no sea la prueba de igualdad o nulidad de referencia.

(*) Los tipos generalmente solo sobrecargan la igualdad para su comparación, pero puede ser útil que los tipos sobrecarguen el operador de igualdad para compararlo con otros tipos particulares; por ejemplo, int podría haber (y en mi humilde opinión debería haberlo hecho) un operador de igualdad para comparar con float, de modo que 16777217 no se reportaría igual a 16777216f. Tal como está, dado que no se define dicho operador, C# promocionará int a float, redondeándolo a 16777216f antes de que el operador de verificación de igualdad lo vea; ese operador luego ve dos números de coma flotante iguales y los reporta como iguales, sin darse cuenta del redondeo que tuvo lugar.

+0

En lugar de tener una comparación int-to-float, el resultado es falso, prefiero el enfoque que usa F #, que es no permitir tal comparación. Entonces el programador puede decidir si los valores tienen un tipo diferente y cómo hacerlo. Porque a veces, después de todo, * hacemos * queremos tratar '3' como siendo igual a' 3.0f'. Si requerimos que el programador diga lo que se pretende en cada caso, entonces no hay peligro de que el comportamiento predeterminado genere resultados no deseados, ya que no existe un comportamiento predeterminado. – phoog

+0

@phoog: Mi sensación personal es que los lenguajes deberían tener sus medios "normales" de prueba de igualdad para implementar una relación de equivalencia, y prohibir todas las combinaciones de operandos para los que no sería así. No veo una gran ventaja de tener una igualdad de verificación de idioma entre enteros y flotantes al confirmar que un flotante representa exactamente un número entero que coincide con el int, versus simplemente prohibir tales comparaciones, pero consideraría que cualquiera de los dos enfoques es mejor que el lenguaje. una conversión con pérdida antes de la comparación. – supercat

-1

Cuando creamos cualquier objeto, hay dos partes en el objeto, una es el contenido y la otra es referencia a ese contenido. == compara contenido y referencia; equals() compara sólo el contenido

http://www.codeproject.com/Articles/584128/What-is-the-difference-between-equalsequals-and-Eq

+1

Esto no es verdad. Si 'a' y' b' son ambas referencias de cadena, entonces el resultado de 'a == b' no depende de si las referencias apuntan al mismo objeto. – phoog

-2

==

El operador == se puede utilizar para comparar dos variables de cualquier tipo, y que simplemente compara los bits de.

int a = 3; 
byte b = 3; 
if (a == b) { // true } 

Nota: hay más ceros en el lado izquierdo de la int pero no se preocupan por eso aquí.

int a (00000011) == byte b (00000011)

Recuerde == operador sólo se preocupa por el patrón de los bits en la variable.

Usar == Si dos referencias (primitivas) se refiere al mismo objeto en el montón.

Las reglas son las mismas si la variable es de referencia o primitiva.

Foo a = new Foo(); 
Foo b = new Foo(); 
Foo c = a; 

if (a == b) { // false } 
if (a == c) { // true } 
if (b == c) { // false } 

a == c es cierto a == b es falsa

el patrón de bits son los mismos para a y c, por lo que son iguales usando ==.

Equal():

Utiliza los método equals() para ver Si dos objetos diferentes son iguales.

tal como dos objetos String diferentes que tanto representar los caracteres en "Jane"

+1

Esto es incorrecto. Considere lo siguiente: 'objeto a = 3; objeto b = 3; Console.WriteLine (a == b); '. La salida es falsa, aunque los patrones de bits de los valores son los mismos. Los tipos de operandos también importan. La razón por la que "no nos importa" la cantidad diferente de ceros en su ejemplo es que cuando llamamos al operador igual, el número de ceros es * realmente el mismo *, debido a la conversión implícita. – phoog

-2

La única diferencia entre igual y == es en la comparación tipo de objeto. en otros casos, como los tipos de referencia y los tipos de valores, son casi los mismos (ambos son iguales a los bits o ambos son igualdad de referencia).

objeto: Igual: bit a bit igualdad ==: la igualdad referencia

cadena: (igual == y son los mismos para cuerda, pero si uno de cadena cambió a oponerse, a continuación, resultado de la comparación serán diferente) Igual a: igualdad de bits ==: igualdad de bits

Consulte here para obtener más información.

+0

Object.Equals no necesariamente considera igualdad de bits. Es un método virtual, y una anulación puede hacer lo que quiera. – phoog

+0

sí, tienes razón, puedes hacer lo que quieras para anularlo. pero el tema del que estamos hablando es la implementación predeterminada. la implementación predeterminada de Object.Equals es la igualdad de bits. –

2

Hay otra dimensión en una respuesta anterior de @BlueMonkMN. La dimensión adicional es que la respuesta a la pregunta del título @ Drahcir como se indica también depende de cómo llegamos al valor string. Para ilustrar:

string s1 = "test"; 
string s2 = "test"; 
string s3 = "test1".Substring(0, 4); 
object s4 = s3; 
string s5 = "te" + "st"; 
object s6 = s5; 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2)); 

Console.WriteLine("\n Case1 - A method changes the value:"); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3)); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4)); 

Console.WriteLine("\n Case2 - Having only literals allows to arrive at a literal:"); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s5), s1 == s5, s1.Equals(s5)); 
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s6), s1 == s6, s1.Equals(s6)); 

La salida es:

True True True 

    Case1 - A method changes the value: 
False True True 
False False True 

    Case2 - Having only literals allows to arrive at a literal: 
True True True 
True True True 
0

realmente grandes respuestas y ejemplos!

me gustaría añadir la diferencia fundamental entre los dos,

Los operadores como == no son polimórficos, mientras Equals es

Con este concepto en mente, si usted se resuelve cualquier ejemplo (al mirar el tipo de referencia de la mano derecha y la mano izquierda, y verificar/saber si el tipo tiene realmente sobrecarga = operador e Igual que sobreescribirlo) está seguro de obtener la respuesta correcta.

6

Por lo que yo entiendo que la respuesta es simple:

  1. == compara las referencias a objetos.
  2. .Equals compara el contenido del objeto.
  3. Los tipos de datos de cadenas siempre actúan como comparación de contenido.

Espero que esté en lo correcto y que haya respondido a su pregunta.

2

Como la versión estática del método .Equal no se mencionó hasta ahora, me gustaría agregar esto aquí para resumir y comparar las 3 variaciones.

MyString.Equals("Somestring"))   //Method 1 
MyString == "Somestring"    //Method 2 
String.Equals("Somestring", MyString); //Method 3 (static String.Equals method) - better 

donde MyString es una variable que viene de alguna otra parte del código.

información de fondo y para summerize:

En Java utilizando == para comparar cadenas no deben utilizarse. Menciono esto en caso de que necesite usar ambos idiomas y también para hacerle saber que el uso de == también se puede reemplazar con algo mejor en C#.

En C# no hay ninguna diferencia práctica para comparar cadenas utilizando el Método 1 o el Método 2, siempre que ambas sean de tipo cadena.Sin embargo, si uno es nulo, uno es de otro tipo (como un entero), o uno representa un objeto que tiene una referencia diferente, entonces, como muestra la pregunta inicial, puede experimentar que comparar el contenido para la igualdad puede no devolver lo que tu esperas.

solución sugerida:

Dado que el uso == no es exactamente lo mismo que usar .Equals al comparar las cosas, se puede utilizar el String.Equals estáticas método en su lugar. De esta forma, si las dos partes no son del mismo tipo, igual compararás el contenido y si uno es nulo, evitarás la excepción.

bool areEqual = String.Equals("Somestring", MyString); 

Es un poco más para escribir, pero en mi opinión, más seguro de usar.

Aquí hay alguna información copiada de Microsoft:

public static bool Equals (string a, string b); 

Parámetros

a cadena

La primera cadena de comparar, o null.

b cadena

La segunda cadena de comparar, o null.

devoluciones Boolean

true si el valor de a es el mismo que el valor de b; de lo contrario, false. Si ambos a y b son null, el método devuelve true.

Cuestiones relacionadas