valores referencias programacion posible numeros lenguaje intencionada comparar comparacion comandos codigos basicos c# ref boxing

c# - referencias - Igualdad de referencia de los tipos de valor



posible comparacion de referencias no intencionada c# (4)

He hecho algunas pruebas de palabras clave de ref y hay uno 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.


Sé que int es un tipo de valor, pero aquí debería pasar referencias al mismo objeto.

Sí, la referencia que se pasa 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: object.ReferenceEquals

Al comparar tipos de valores. Si objA y objB son tipos de valor, están objB objA objB al método ReferenceEquals . Esto significa que si tanto objA como objB representan la misma instancia de un tipo de valor, el método ReferenceEquals , sin embargo, devuelve false


¿Por qué este código muestra False ?

Porque int a e int b están siendo object.ReferenceEquals cuando llamas a object.ReferenceEquals . Cada entero está encuadrado dentro de una instancia de object . Por lo tanto, en realidad estás comparando referencias entre dos valores en caja, 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 unsafe 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 puedes 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 busca la misma referencia, pero es aún más minucioso, ya que se asegura de que ambas sean la misma ''ubicación'' (o que estén referenciadas en la misma variable). 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.


La confusión aquí se debe a que, a diferencia de los punteros (como en *), "ref" en C # no es una parte de un tipo, sino una parte de una firma de método. Se aplica al parámetro y significa "no debe copiarse". No significa "este argumento tiene tipo de referencia".

El parámetro pasado por ref, en lugar de representar una nueva ubicación de almacenamiento, es en cambio un alias para alguna ubicación existente. Cómo se crea el alias es técnicamente un detalle de implementación. La mayoría de los alias se implementan como referencias gestionadas, pero no siempre. En algunos casos relacionados con async, por ejemplo, una referencia a un elemento de matriz podría representarse internamente como una combinación de matriz e índice.

Esencialmente para todos los propósitos, a y b aún son entendidos por C # como variables int-tipeadas. Es legal y completamente normal usarlos en cualquier expresión que tome valores int como a + b, o SomeMethod (a, b) y en esos casos se usan los valores int reales almacenados en ayb.

Realmente no hay concepto de una "referencia" como una entidad con la que pueda trabajar directamente en C #. A diferencia de los punteros, debe asumirse que los valores reales de las referencias administradas pueden cambiar en cualquier momento, o incluso de forma asíncrona, por GC, por lo que el conjunto de escenarios significativos en las referencias administradas sería extremadamente limitado.