La igualdad de objetos se comporta diferente en .NET

Tengo estas declaraciones y sus resultados están cerca de ellos.

string a = "abc"; string b = "abc"; Console.Writeline(a == b); //true object x = a; object y = b; Console.Writeline(x == y); // true string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'}); Console.Writeline(c == d); // true object k = c; object m = d; Console.Writeline(k.Equals(m)) //true Console.Writeline(k == m); // false 

¿Por qué la última igualdad me da falsa?

La pregunta es por qué (x == y) es verdadero (k == m) es falso

 string a = "abc"; string b = "abc"; Console.Writeline(a == b); //true 

Las referencias de cadena son las mismas para la misma cadena debido a String Interning

 object x = a; object y = b; Console.Writeline(x == y); // true 

Debido a que las referencias son las mismas, dos objetos creados a partir de la misma referencia también son iguales.

 string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'}); 

Aquí creas dos NUEVAS matrices de caracteres, estas referencias son diferentes.

 Console.Writeline(c == d); // true 

Las cadenas se han sobrecargado == para comparar por valor.

 object k = c; object m = d; 

Dado que las referencias anteriores son diferentes, estos objetos son diferentes.

 Console.Writeline(k.Equals(m)) //true 

.Equals usa el método sobrecargado de String .Equals , que de nuevo se compara por valor

 Console.Writeline(k == m); // false 

Aquí verificamos si las dos referencias son iguales … no son

La clave es averiguar cuándo una igualdad es comparar referencias o valores.

Los objetos, a menos que estén sobrecargados, comparan las referencias.

Las estructuras, a menos que estén sobrecargadas, comparan valores.

En el caso de cadenas, el operador == está sobrecargado para probar la igualdad de valores, cuando se utiliza la igualdad de referencia de object .

Dado que c y d son cadenas, cuando se usa Equals en k y m , se usa el método de sobrecarga.

Y c == d es true para el estado de razón anterior: la igualdad de valores se usa en string tipos de string medida que el operador está sobrecargado.

Porque son dos referencias de objetos diferentes. La comparación integrada para esto es comparar si apuntan al mismo objeto real o no.

Debido a String Interning , a y b son referencias al mismo objeto de cadena.

c==d es verdadero porque se está utilizando el operador de igualdad de cadena.

  string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'}); Console.WriteLine(c == d); // true object k = c; object m = d; Console.WriteLine(k.Equals(m)); //true Console.WriteLine(k == m); // false 

Genera código IL, como este:

 IL_0001: ldc.i4.3 IL_0002: newarr System.Char IL_0007: dup IL_0008: ldtoken {61BB33F4-0CA5-4733-B259-764124AD1A79}.$$method0x6000002-1 IL_000D: call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray IL_0012: newobj System.String..ctor IL_0017: stloc.0 IL_0018: ldc.i4.3 IL_0019: newarr System.Char IL_001E: dup IL_001F: ldtoken {61BB33F4-0CA5-4733-B259-764124AD1A79}.$$method0x6000002-2 IL_0024: call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray IL_0029: newobj System.String..ctor IL_002E: stloc.1 IL_002F: ldloc.0 IL_0030: ldloc.1 IL_0031: call System.String.op_Equality //STRING EQUALITY IL_0036: call System.Console.WriteLine IL_003B: nop IL_003C: ldloc.0 IL_003D: stloc.2 IL_003E: ldloc.1 IL_003F: stloc.3 IL_0040: ldloc.2 IL_0041: ldloc.3 IL_0042: callvirt System.Object.Equals IL_0047: call System.Console.WriteLine IL_004C: nop IL_004D: ldloc.2 IL_004E: ldloc.3 IL_004F: ceq //CEQ INSTRUCTION: **VALUES** EQUALITY ! IL_0051: call System.Console.WriteLine 

Como puede ver, la última instrucción llama a la instrucción CEQ que hace la comparación de la igualdad de valores empujada en la stack. Los valores empujados en la stack son referencias de ambas cadenas encajonadas, que no son iguales.

Como se ve en C # Preguntas frecuentes en MSDN : el comstackdor no puede usar el método sobrecargado y vuelve a comparar las referencias.

La pregunta más importante es por qué tiene éxito en la primera comparación de objetos. Mi mejor conjetura es que tiene éxito porque a y b se les da la misma referencia. Para cyd, estás forzando diferentes referencias.

String ha sobrecargado el operador de igualdad para que pueda usar == para la comparación de valores. Por lo tanto

a == b //true .

Cuando los estás reduciendo a objeto, solo estás comparando referencias. String busca en un grupo de cadenas interno si ya hay otra instancia de cadena disponible; de ​​lo contrario, se creará una nueva instancia y se agregará a la agrupación. Así que en realidad a , b , y son incluso la misma referencia, por eso

x == y //true .

Con el uso del constructor de String , obliga a .NET a crear una nueva instancia incluso si existe otra cadena con el mismo valor (longitud y secuencia de caracteres). Es por eso

k == m //false

http://en.csharp-online.net/CSharp_String_Theory%E2%80%94String_intern_pool

Cuando dice string1 == string2 , la comparación usa el operador sobrecargado == del tipo de string , que compara los valores de las cadenas.

Cuando dice object1 == object2 , aunque sean cadenas en este caso, no se calificarán como cadenas con el fin de encontrar un operador. Así que la comparación utiliza el operador predeterminado == , que compara las referencias de igualdad. Lo que significa que, si los dos objetos no son exactamente el mismo objeto, devolverá falso.

Sobre la base de Bob2Chiv, probé el equivalente en VB (VS2010):

  Dim a As String = "abc" Dim b As String = "abc" Console.WriteLine(a = b) ' True Dim x As Object = a Dim y As Object = b Console.WriteLine(x = y) ' True Dim c As String = New String(New Char() {"a"c, "b"c, "c"c}) Dim d As String = New String(New Char() {"a"c, "b"c, "c"c}) Console.WriteLine(c = d) ' True Dim k As Object = c Dim m As Object = d Console.WriteLine(k.Equals(m)) ' True Console.WriteLine(k = m) ' True (Yes, True!!) Console.WriteLine(k Is m) ' False (Like in C#) Console.WriteLine(a Is b) ' True (Like in C#) 

(Al menos creo que es el equivalente, me complace que me corrijan esto).

¿Moral ?: Tenga cuidado con == en C # – prefiera .Equals() cuando la comparación de valores es lo que se desea?

El operador == compara las referencias (las direcciones de memoria) mientras que .Equals compara los valores reales del objeto. En el caso de la cadena, tiene suerte y dos cadenas idénticas pueden referirse a la misma dirección con frecuencia. Una de las alegrías de los lenguajes gestionados.