2009-08-08 12 views
10

he preguntado a menudo si el siguiente escenario que realmente sucede en C#de boxeo en estructuras al llamar ToString()

Si tengo una estructura pero no anula explícitamente cualquiera de los métodos que se derivan de objeto, como ToString(), GetHashCode(), etc. entonces si declaro una instancia local de mi clase struct y llamo 'ToString()' a ella, mi estructura sería encasillada, es decir, la CLR la convertiría implícitamente en un objeto en el montón y luego llamar a ToString()? ¿O es lo suficientemente inteligente como para saber que no hay implementación para esa estructura e ignorarla?

es decir

public struct Vector2D 
{ 
    public float m_x; 
    public float m_y; 


    ...... etc 
} 


void SomeFunc() 
{ 
    Vector2D aVec = new Vector2D(); 
    Console.WriteLine(aVec.ToString()); // <-- does aVec get boxed here? 
    ..... 
} 

== Editar - Actualización == de Mehrdad link to MSDN, siendo a la vez útil me ha confundido ligeramente. Voy a citar a ver si alguien puede deshacer esto por mí

Cuando una instrucción método callvirt se ha restringido el prefijo thisType, la instrucción se ejecuta de la siguiente manera:

Si se thisType un tipo de referencia (como opuesto a un tipo de valor), entonces ptr es desreferenciado y pasado como el puntero 'this' al callvirt del método.

Si thisType es un tipo de valor y thisType implementa el método a continuación, ptr es pasado sin modificar como el 'esto' puntero a una instrucción de llamada de método, para la aplicación del método por el thisType.

Si thisType es un tipo de valor y thisType no implementa el método entonces PTR se eliminan las referencias, en caja, y pasa como este puntero a la instrucción método callvirt.

¿Eso quiere decir que si no se implementa de forma explícita ToString() en mi tipo de estructura que va a caer en el último caso y obtener en caja? ¿O lo estoy entendiendo mal en alguna parte?

+0

posible duplicado de [¿Llamar a un método en un tipo de valor resulta en el boxeo en .NET?] (Http://stackoverflow.com/questions/436363/does-calling-a-method-on-a-value- type-result-in-boxing-in-net) – nawfal

Respuesta

8

Si thisType es un tipo de valor y thisType no implementa el método entonces PTR se eliminan las referencias, en caja, y pasa como este puntero a la instrucción método callvirt.

Este último caso sólo puede ocurrir cuando se definió método de Object, ValueType, o Enum y no se reemplaza por thisType. En este caso, el boxeo hace que se realice una copia del objeto original .

La respuesta es sí, el tipo de valor es en caja. Es por eso que siempre es bueno anular ToString() en estructuras personalizadas.

+0

Sip, mira mi publicación editada - Creo que tú ' Re justo en este – zebrabox

+0

+1 para leer la pregunta correctamente. –

+1

@zebrabox: a menos que no llame 'ToString()' al objeto. :) –

4

, no lo es en caja cuando se llama ToString o GetHashCode si es implementado por su estructura (why should it? constrained IL instruction takes care of it.) Está en caja cuando se llama a un método no virtual (o un método virtual no anulado en el struct) en System.Object (su base clase), es decir, GetType/MemberwiseClone.

ACTUALIZACIÓN: Disculpe el malentendido que puede haber causado. Escribí la respuesta anulando los métodos en la estructura en mente (es por eso que mencioné que los métodos no virtuales necesitan boxeo, debería haber sido más explícito para no confundir a los lectores, especialmente porque me perdí su declaración sobre no anular el método) como si no lo anula, el método Object.ToStringespera su primer argumento (referencia a this) es un tipo de referencia (una instancia Object). Obviamente, el valor debe encasillarse en esa llamada (ya que es una llamada en la clase base).)

Sin embargo, el punto es, la naturaleza de llamar a un método virtual en un valor de tipo no lo hace resultado en emitir la instrucción box (a diferencia de los métodos no-virtuales en Object que siempre dan lugar a la emisión de una instrucción explícita box.) Es la instrucción callvirt que hará el boxeo si tiene que recurrir a la implementación Object.ToString (como mencionaste en la pregunta actualizada) igual que cuando pasas una estructura a un método que espera un parámetro object.

+0

Gracias por el enlace v útil – zebrabox

+0

-1 Definitivamente no es cierto. Ver mi respuesta –

+0

El punto es: ¿se encasilla? La pregunta quién/qué inició la operación de cuadro es insignificante en comparación con el rendimiento real de operación de cuadro, que es el problema aquí. –

7

Editar:kek444's answer es correcto. Mis disculpas por haber malinterpretado la pregunta. Dejo mi respuesta aquí, ya que creo que tiene un valor adicional e información relevante para futuros lectores.

También creo que esta cita del reference en Mehrdad's answer particular se invita a la reflexión:

  • Si thisType es un tipo de valor y thisType no implementa el método entonces PTR se eliminan las referencias, en caja, y pasado como el puntero 'this' a la instrucción de método callvirt .

Este último caso sólo puede ocurrir cuando se definió método sobre el objeto, ValueType o ENUM y no anulado por thisType. En este caso, el boxeo hace que se realice una copia del objeto original . Sin embargo, como ninguno de los métodos de Object, ValueType y Enum modifican el estado del objeto, este hecho no se puede detectar.

Uno no puede, por lo tanto, escribir un programa para demostrar que el boxeo está llevando a cabo. Solo es discernible al observar el IL y comprender completamente el prefijo constrained para la instrucción callvirt.


Desde la sección 11.3.5 de la especificación de lenguaje C# en http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20Language%20Specification.doc (http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx):

Cuando un tipo struct anula un método virtual heredado de System.Object (tales como iguales, GetHashCode, o ToString) , la invocación del método virtual a través de una instancia del tipo de estructura no provoca que se produzca el boxeo. Esto es cierto incluso cuando la estructura se utiliza como un parámetro de tipo y la invocación se produce a través de una instancia del tipo de parámetro de tipo. Por ejemplo:

using System; 
struct Counter 
{ 
    int value; 
    public override string ToString() { 
     value++; 
     return value.ToString(); 
    } 
} 
class Program 
{ 
    static void Test<T>() where T: new() { 
     T x = new T(); 
     Console.WriteLine(x.ToString()); 
     Console.WriteLine(x.ToString()); 
     Console.WriteLine(x.ToString()); 
    } 
    static void Main() { 
     Test<Counter>(); 
    } 
} 

La salida del programa es:

1 
2 
3 

pesar de que es un mal estilo de ToString que tienen efectos secundarios, el ejemplo demuestra que ninguna de boxeo se produjo a las tres invocaciones de x. Encadenar().

De forma similar, el boxeo nunca se produce implícitamente al acceder a un miembro en un parámetro de tipo restringido.Por ejemplo, supongamos que una interfaz ICounter contiene un método Increment que puede usarse para modificar un valor. Si ICounter se utiliza como una restricción, se llama a la implementación del método Increment con una referencia a la variable a la que se llamó el Incremento, nunca una copia encuadrada.

using System; 
interface ICounter 
{ 
    void Increment(); 
} 
struct Counter: ICounter 
{ 
    int value; 
    public override string ToString() { 
     return value.ToString(); 
    } 
    void ICounter.Increment() { 
     value++; 
    } 
} 
class Program 
{ 
    static void Test<T>() where T: ICounter, new() { 
     T x = new T(); 
     Console.WriteLine(x); 
     x.Increment();      // Modify x 
     Console.WriteLine(x); 
     ((ICounter)x).Increment();  // Modify boxed copy of x 
     Console.WriteLine(x); 
    } 
    static void Main() { 
     Test<Counter>(); 
    } 
} 

La primera llamada a Incremento modifica el valor en la variable x. Esto no es equivalente a la segunda llamada a Incremento, que modifica el valor en una copia en caja de x. Por lo tanto, la salida del programa es:

0 
1 
1 

Para más detalles sobre el boxeo y unboxing, ver § 4.3.

+0

Gracias por la respuesta, pero mi pregunta era en realidad qué pasaría si no invalidaba toString() – zebrabox

+0

Sip, es bueno tener que conocer el IL. No emite explícitamente una instrucción de cuadro por lo que la 'caja' está ocurriendo como resultado de llamar a la implementación de la clase base a través de la instancia de cadena callcallvirt [mscorlib] System.Object :: ToString() – zebrabox