2010-06-05 10 views
7

I this MSDN Magazine article, el autor (el énfasis es mío):¿El desempaquetado solo devuelve un puntero al valor dentro del objeto en caja en el montón?

Note que el boxeo siempre crea un nuevo objeto y copias los bits del valor sacó de la caja al objeto. Por otro lado, unboxing simplemente devuelve un puntero a los datos dentro de un objeto en caja: no se produce la copia de memoria . Sin embargo, es comúnmente el caso en que su código será porque los datos apuntados por la referencia sin caja se copiarán de todos modos.

Estoy confundido por la oración que he en negrita y la oración que sigue. De todo lo demás que he leído, incluido el this MSDN page, nunca antes había escuchado que el desempaquetado solo devuelve un puntero al valor en el montón. Tenía la impresión de que unboxing daría como resultado que tengas una variable que contenga una copia del valor en la pila, tal como comenzaste. 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.

¿Alguien puede explicar lo que esto significa? ¿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 los que "su código hará que los datos señalados por la referencia unboxed se copien de todos modos"?

Me acabo de dar cuenta de que el artículo tiene casi 10 años, así que tal vez esto es algo que cambió muy temprano en la vida de .Net.

Respuesta

6

El artículo es exacto. Sin embargo, habla sobre qué realmente continúa, no cómo se ve la IL que genera el compilador. Después de todo, un programa .NET nunca ejecuta IL, sino que ejecuta el código máquina que genera el IL por el compilador JIT.

Y el código de operación unbox genera 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 auxiliar en el CLR denominada "JIT_Unbox". clr \ src \ vm \ jithelpers.cpp si tiene el código fuente SSCLI20. La función Object :: GetData() devuelve el puntero.

A partir de ahí, el valor más común primero se copia en un registro de la CPU. Que luego puede ser almacenado en alguna parte. No tiene que ser la pila, podría ser un miembro de un objeto de tipo de referencia (el montón de 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 la CPU podría usarse tal como está cuando el valor se usa en una expresión.

Al realizar la depuración, haga clic con el botón derecho en la ventana del editor y seleccione "Ir al desmontaje" para ver el código de la máquina.

+0

¡Ya veo! Gracias. –

1

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

De acuerdo con 'C# 4.0 in a Nutshell': "... unboxing copia el contenido del objeto en una instancia de tipo de valor" y eso implica en la pila.

En el artículo se hace referencia, el autor afirma:

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" 
} 

A partir de este código, se puede adivinar cuántos se producen operaciones de boxeo? ¡Puede ser sorprendido al descubrir que la respuesta es tres! Analicemos el código cuidadosamente para comprender realmente qué está pasando . Primero, se crea e inicializa un tipo de valor de Int32 unboxed (v) a 5. Luego se crea un tipo de referencia de objeto (o) y apunta a v. Pero los tipos de referencia siempre deben señalar a los objetos en el montón , así que C# generó el código IL apropiado para el cuadro v y almacenó la dirección de la versión en caja de v en o. Ahora 123 es unboxed y los datos referenciados se copian en el tipo de valor unboxed v; esto no tiene efecto en la versión en caja de v, entonces la versión en caja mantiene su valor de 5. Tenga en cuenta que este ejemplo muestra cómo o se desempaqueta (que devuelve un puntero a los datos en o), y luego el los datos en o se copian en la memoria al valor 0xsin clasificar v.

+0

Sí, todo lo que he leído dice o implica que los datos se copian nuevamente a la pila. En cuanto a esta explicación que se contradice a sí misma: sí, y la parte de la cita que pegó en negrita es en realidad a lo que me refería cuando dije que había un error en el artículo. Es absolutamente incorrecto: o no está desvinculado en cualquier lugar del ejemplo. –

5

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, regarding unbox.any:

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

y regarding unbox:

[...] unbox no es necesario copiar el tipo de valor del objeto. Normalmente, simplemente calcula la dirección del tipo de valor que es ya presente dentro del objeto en caja.

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

Este pequeño hecho sobre unbox hace posible ciertas optimizaciones ingeniosas cuando se trabaja directamente con IL. Por ejemplo, si tiene un recuadro int que necesita pasar a una función que acepta una ref int, puede simplemente emitir un código de operación unbox, y la referencia a int estará lista en la pila para que funcione la función. 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 la necesidad de asignar espacio para una variable local temporal, desempaquetar el int, pasar una referencia al int de la función y luego crear un nuevo objeto de boxeo para volver a insertar el int, descartando el cuadro anterior.

Por supuesto, cuando trabajas en el nivel C#, no puedes hacer ninguna de esas optimizaciones, entonces lo que normalmente sucederá es que el código generado por el compilador casi siempre copiará la variable del objeto encuadrado antes de haciendo cualquier uso adicional de ella.

+0

+1 Gran adición, gracias. –

Cuestiones relacionadas