2010-09-18 12 views
6

Una pregunta simple, pero no he encontrado una respuesta definitiva en Stack Overflow.¿Alguna vez se encasilla una estructura de C# cuando se usa como el valor de retorno de una función?

struct foo { int x; int y; int z; } 

    foo Func() 
    { 
     return new foo(); 
    } 
    void Func2() 
    { 
     foo f = Func();  // did boxing and unboxing occur? 
    } 

Es una estructura de C# (tipo de valor) siempre copian en la pila cuando devueltas por una función, no importa lo grande que podría ser? La razón por la que no estoy seguro es que para algunos conjuntos de instrucciones que no sean MSIL (como x86), un valor de retorno por lo general debe caber en un registro de procesador, y la pila no está directamente involucrada.

En caso afirmativo, ¿es el sitio de llamadas el que preasigna espacio en la pila CLR para el tipo de valor devuelto (esperado)?

[Editar: resumen de respuestas:] Para el propósito de la pregunta original, la respuesta es no; el CLR nunca (silenciosamente) encajonará una estructura solo con el propósito de enviarla como un valor de retorno.

+0

@Brian: gracias, me doy cuenta de eso, pero creo que estás diciendo que el proceso de devolverlo desde la función en sí no implica ningún boxeo. He actualizado el código para aclarar la pregunta. –

+0

Genial, gracias por tus respuestas! –

+0

Si la devolución de un tipo de valor es alguna vez (su uso de "alguna vez" parece implicar que, a veces, si sucede, no es incondicional), significará que hay unboxing implícito cuando asignamos el retorno a una variable de tipo de valor, y eso parece ineficiente. No sé mucho sobre MSIL y CLR, así que no puedo ayudarte aquí. De todos modos, ¿puedo saber qué provocó tu pregunta? – blizpasta

Respuesta

6

Es un detalle de implementación pesado del compilador JIT. En general, si la estructura es lo suficientemente pequeña y tiene miembros simples, entonces se devuelve en los registros de la CPU. Si se vuelve demasiado grande, el código de llamada reserva suficiente espacio en la pila y pasa un puntero a ese espacio como un argumento oculto adicional.

Nunca se incluirá en la caja, a menos que el tipo de devolución del método sea objeto por supuesto.

Fwiw: esta es también la razón por la que el depurador no puede mostrar el valor de retorno de la función en la ventana Autos. Doloroso a veces Pero el depurador no obtiene suficientes metadatos del compilador JIT para saber exactamente dónde encontrar el valor. Editar: arreglado en VS2013.

6

Una estructura está encasillada cada vez que desee tratarla como object, por lo que si llama al Func y asigna el resultado al objeto, aparecerá en recuadro.

E.g. haciendo esto

object o = Func(); 

producirá el siguiente IL

L_0000: call valuetype TestApp.foo TestApp.Program::Func() 
L_0005: box TestApp.foo 
L_000a: stloc.0 

que muestra que el valor de retorno es en caja, porque asignamos a una referencia del tipo object.

Si lo asigna a una variable del tipo Foo, no está encuadrado y, por lo tanto, se copia y el valor se almacena en la pila.

Además, el boxeo realmente no lo ayudaría, ya que implicaría crear un objeto para representar el valor de la estructura, y los valores se copiarán efectivamente durante la operación de boxeo.

+1

El hecho de que el sitio de llamadas debe asignar el número apropiado de bytes en la pila es la razón por la cual las pautas de diseño de .NET sugieren que 'struct's idealmente no debería ser mayor de 16 bytes. –

+1

@Richard: ¿Tiene una fuente para esa información? –

+0

Revisa http://msdn.microsoft.com/en-us/library/y23b5415(VS.71).aspx –

Cuestiones relacionadas