Hice algunas pruebas de palabras clave ref
y hay una cosa que no puedo entender:
static void Test(ref int a, ref int b) { Console.WriteLine(Int32.ReferenceEquals(a,b)); } static void Main(string[] args) { int a = 4; Test(ref a, ref a); Console.ReadLine(); }
¿Por qué este código muestra False
? Sé que int
es un tipo de valor, pero aquí debería pasar referencias al mismo objeto.
¿Por qué este código muestra
False
?
Porque int a
e int b
están encuadrados cuando llamas a object.ReferenceEquals
. Cada entero está encuadrado dentro de una instancia de object
. Por lo tanto, en realidad está comparando referencias entre dos valores encuadrados, que claramente no son iguales.
Puede ver esto fácilmente si observa el CIL generado para el método:
Test: IL_0000: nop IL_0001: ldarg.0 Load argument a IL_0002: ldind.i4 IL_0003: box System.Int32 IL_0008: ldarg.1 Load argument b IL_0009: ldind.i4 IL_000A: box System.Int32 IL_000F: call System.Object.ReferenceEquals IL_0014: call System.Console.WriteLine IL_0019: nop IL_001A: ret
La verificación de la igualdad de ubicación de almacenamiento se puede lograr mediante el uso de CIL verificable (como en la respuesta de @leppie ) o mediante un código unsafe
:
unsafe static void Main(string[] args) { int a = 4; int b = 5; Console.WriteLine(Test(ref a, ref a)); // True Console.WriteLine(Test(ref a, ref b)); // False; } unsafe static bool Test(ref int a, ref int b) { fixed (int* refA = &a) fixed (int* refB = &b) { return refA == refB; } }
Esto no se puede hacer directamente en C#.
Sin embargo, puede implementarlo en CIL verificable:
.method public hidebysig static bool Test<T>(!!T& a, !!T& b) cil managed { .maxstack 8 ldarg.0 ldarg.1 ceq ret }
Pruebas
int a = 4, b = 4, c = 5; int* aa = &a; // unsafe needed for this object o = a, p = o; Console.WriteLine(Test(ref a, ref a)); // True Console.WriteLine(Test(ref o, ref o)); // True Console.WriteLine(Test(ref o, ref p)); // False Console.WriteLine(Test(ref a, ref b)); // False Console.WriteLine(Test(ref a, ref c)); // False Console.WriteLine(Test(ref a, ref *aa)); // True // all of the above works for fields, parameters and locals
notas
En realidad, esto no verifica la misma referencia, pero es aún más detallado en el sentido de que se asegura de que ambos estén en la misma 'ubicación' (o referenciados desde la misma variable) también. Esto es mientras que la tercera línea devuelve false
aunque o == p
devuelve true
. Sin embargo, la utilidad de esta prueba de 'ubicación' es muy limitada.
Lo sé, ese int es un tipo de valor, pero aquí debería pasar referencias al mismo objeto.
Sí, la referencia pasada al método es la misma, pero están encuadradas (convertidas a tipo de objeto/referencia) en el método ReferenceEquals
.
Es por eso que el resultado de su prueba devuelve falso, ya que está comparando referencias de dos objetos diferentes, debido al boxeo .
Ver: Método Object.ReferenceEquals
Al comparar tipos de valores. Si
objA
yobjB
son tipos de valor, se encuadran antes de pasar al métodoReferenceEquals
. Esto significa que si tantoobjA
comoobjB
representan la misma instancia de un tipo de valor, el métodoReferenceEquals
devuelve falso.