¿Unboxing simplemente devuelve un puntero al valor dentro del objeto en caja en el montón?

StackOverflow https://stackoverflow.com/questions/2978647

  •  24-10-2019
  •  | 
  •  

Pregunta

yo Este artículo de la revista MSDN, el autor dice (énfasis mía):

Tenga en cuenta que el boxeo siempre crea un nuevo objeto y copia los bits del valor sin caja al objeto. Por otro lado, Unboxing simplemente devuelve un puntero a los datos dentro de un objeto en caja: no se produce una copia de memoria. Sin embargo, comúnmente es el caso de que su código haga que los datos apuntados por la referencia sin caja se copie de todos modos.

Estoy confundido por la oración que he en negrita y la oración que la sigue. De todo lo demás que he leído, incluido Esta página de MSDN, Nunca antes escuché que el Unboxing solo devuelve un puntero al valor en el montón. Tenía la impresión de que la unboxing resultaría en que tenga una variable que contenga una copia del valor en la pila, tal como comenzó. Después de todo, si mi variable contiene "un puntero al valor en el montón", entonces no tengo un tipo de valor, tengo un puntero.

puede alguien explicarme que significa esto? ¿Estaba el autor en Crack? (Hay al menos otro error evidente en el artículo). Y si esto es cierto, ¿cuáles son los casos en que "su código hará que los datos apuntados por la referencia sin caja se copiaran de todos modos"?

Acabo de notar que el artículo tiene casi 10 años, por lo que tal vez esto es algo que cambió muy temprano en la vida de .NET.

¿Fue útil?

Solución

El artículo es preciso. Sin embargo, habla de lo que De Verdad Continúa, no cómo se ve el compilador que genera el compilador. Después de todo, un programa .NET nunca ejecuta IL, ejecuta el código de la máquina generado a partir del IL por el compilador JIT.

Y el código de operación Unbox genera un código que produce un puntero a los bits en el montón que representa el valor de tipo de valor. El JIT genera una llamada a una pequeña función de ayudante en el CLR llamado "jit_unbox". CLR SRC VM JITHELPERS.CPP Si obtuvo el código fuente SSCLI20. La función objeto :: getData () devuelve el puntero.

A partir de ahí, el valor más comúnmente se copia primero en un registro de CPU. Que luego puede almacenarse en alguna parte. No tiene que ser la pila, podría ser un miembro de un objeto de tipo de referencia (el montón GC). O una variable estática (el montón del cargador). O podría ser empujado en la pila (llamada de método). O el registro de CPU podría usarse como es cuando el valor se usa en una expresión.

Mientras depuración, haga clic con el botón derecho en la ventana del editor y elija "Vaya a desmontaje" para ver el código de la máquina.

Otros consejos

El autor del artículo original debe haberse referido a lo que está sucediendo en el nivel IL. Existen dos códigos de operación de Unboxing: unbox y unbox.any.

Según MSDN, con respecto a unbox.any:

Cuando se aplica a la forma en caja de un tipo de valor, la Unbox. cualquier instrucción extrae el valor contenido dentro de OBJ (del tipo O) y, por lo tanto, es equivalente a unbox seguido de LDOBJ.

y con respecto a unbox:

...] Unbox no es necesario para copiar el tipo de valor del objeto. Por lo general, simplemente calcula la dirección del tipo de valor que ya está presente dentro del objeto en caja.

Entonces, el autor sabía de lo que estaba hablando.

Este pequeño hecho sobre unbox hace posible hacer ciertas optimizaciones ingeniosas cuando se trabaja directamente con IL. Por ejemplo, si tiene un int en caja que necesita pasar a una función que acepta una reft, solo puede emitir un unbox Opcode, y la referencia al int estará lista en la pila para que la función funcione. En este caso, la función cambiará el contenido real del objeto de boxeo, algo que es bastante imposible en el nivel de C#. Le ahorra de la necesidad de asignar espacio para una variable local temporal, unbox el int allí, pase una referencia a la función int a la función y luego cree un nuevo objeto de boxeo para volver a boxear el int, descartar la caja anterior.

Por supuesto, cuando trabaja en el nivel C#, no puede hacer tales optimizaciones, por lo que lo que generalmente sucederá es que el código generado por el compilador casi siempre copiará la variable del objeto en caja antes de hacer cualquier uso adicional. de eso.

El boxeo es el acto de lanzar una instancia de tipo de valor a una instancia de tipo de referencia (ya sea una object o una interfaz), y los tipos de referencia se asignan en el montón.

Según 'C# 4.0 en pocas palabras': "... Unboxing copias El contenido del objeto nuevamente en una instancia de tipo de valor "y eso implica en la pila.

En el artículo que hace referencia, el autor dice:

public static void Main() {

   Int32 v = 5;    // Create an unboxed value type variable
   Object o = v;   // o refers to a boxed version of v
   v = 123;        // Changes the unboxed value to 123

   Console.WriteLine(v + ", " + (Int32) o);    // Displays "123, 5"
}

De este código, ¿puedes adivinar cuántas operaciones de boxeo ocurren? ¡Es posible que se sorprenda al descubrir que la respuesta es tres! Analicemos el código cuidadosamente para comprender realmente lo que está sucediendo. Primero, se crea e inicializa un tipo de valor sin caja int32 (v) a 5. Luego se crea un tipo de referencia de objeto (o) y desea apuntar a v. Pero los tipos de referencia siempre deben apuntar a los objetos en el montón, por lo que c# generado El código IL adecuado para el cuadro V y almacenó la dirección de la versión en caja de V en o. Ahora 123 no está en caja y los datos referenciados se copian en el tipo de valor sin caja V; Esto no tiene ningún efecto en la versión en caja de V, por lo que la versión en caja mantiene su valor de 5. Tenga en cuenta que este ejemplo muestra cómo O no está en caja (que devuelve un puntero a los datos en o), y luego los datos en O son la memoria copiada al tipo de valor sin caja v.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top