2010-07-21 13 views
15

que ejecuta el siguiente código:C# El tipo genérico está encuadrado?

using System; 
using System.Collections.Generic; 

namespace TestReleaseAndDebug 
{ 
    public class GClass<T1, T2> 
    { 
     public T1 Name { get; set; }  
     public T2 Age { get; set; } 

     public void Display() 
     { 
      Console.WriteLine("Name: " + Name);   
      Console.WriteLine("Age: " + Age); 
     } 
    } 

    class Program 
    {   
     static void Main(string[] args) 
     { 
      GClass<string, int> person = new GClass<string, int>(); 
      person.Name = "RAM";   
      person.Age = 34; 
      string name = "RAM";   
      int age = 34; 

      Console.WriteLine("Name: " + name);   
      Console.WriteLine("Age: " + age);   
      person.Display(); 

      Console.Read(); 
     } 
    } 
} 

Estoy teniendo dos variables locales de la función principal son el nombre y la edad. Los estoy imprimiendo usando el método console.writeline. Se imprime sin ningún problema. El IL del método principal es el siguiente:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  90 (0x5a) 
    .maxstack 2 
    .locals init ([0] class TestReleaseAndDebug.GClass`2<string,int32> person, 
      [1] string name, 
      [2] int32 age) 
    IL_0000: nop 
    IL_0001: newobj  instance void class TestReleaseAndDebug.GClass`2<string,int32>::.ctor() 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: ldstr  "RAM" 
    IL_000d: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Name(!0) 
    IL_0012: nop 
    IL_0013: ldloc.0 
    IL_0014: ldc.i4.s 34 
    IL_0016: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Age(!1) 
    IL_001b: nop 
    IL_001c: ldstr  "RAM" 
    IL_0021: stloc.1 
    IL_0022: ldc.i4.s 34 
    IL_0024: stloc.2 
    IL_0025: ldstr  "Name: " 
    IL_002a: ldloc.1 
    IL_002b: call  string [mscorlib]System.String::Concat(string, 
                   string) 
    IL_0030: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_0035: nop 
    IL_0036: ldstr  "Age: " 
    IL_003b: ldloc.2 
    IL_003c: box  [mscorlib]System.Int32 
    IL_0041: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0046: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_004b: nop 
    IL_004c: ldloc.0 
    IL_004d: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::Display() 
    IL_0052: nop 
    IL_0053: call  int32 [mscorlib]System.Console::Read() 
    IL_0058: pop 
    IL_0059: ret 
} // end of method Program::Main 

Tengo otra clase genérica 'GClass'. En la clase genérica, tengo dos propiedades y un método (Pantalla). En el método de visualización, muestro las dos propiedades de la misma manera en que visualicé las variables locales en el método principal. La IL del método Display clase genérica se da a continuación:

.method public hidebysig instance void Display() cil managed 
{ 
    // Code size  56 (0x38) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldstr  "Name: " 
    IL_0006: ldarg.0 
    IL_0007: call  instance !0 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Name() 
    IL_000c: box  !T1 
    IL_0011: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0016: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_001b: nop 
    IL_001c: ldstr  "Age: " 
    IL_0021: ldarg.0 
    IL_0022: call  instance !1 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Age() 
    IL_0027: box  !T2 
    IL_002c: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0031: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_0036: nop 
    IL_0037: ret 
} // end of method GClass`2::Display 

estoy pasando 'cadena' como el parámetro de tipo de T1 y el uso de este tipo para declarar propiedad Name. Cuando la propiedad del nombre se muestra usando Console.Writeline, está encajonando el nombre (IL_000c: box! T1). Puedes encontrar esto en IL.

¿Por qué el boxeo ocurre a pesar de que es un tipo de cadena?

+1

Eso es mucho código para tener en una publicación. Debería considerar reducir los fragmentos de IL a las líneas que demuestran su pregunta. –

+0

También verifique esto: http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers –

Respuesta

6

Esto es así porque compilador no está seguro de que tanto T1 y T2 siempre habría un tipo de referencia o el valor de tipo. por lo tanto, los pone en Objeto por defecto para ambos casos cuando T1 o T2, cualquiera de ellos es un tipo de valor o tipo de referencia.

El tipo Object puede actuar en la dualidad. Puede box-unbox para los tipos de valor y mantener las referencias a las instancias de cualquier subclase tipos cuando es un tipo de referencia.

Entonces, cuando T1 es una cadena, en realidad no es boxeo, contiene la referencia de la instancia de cadena porque Object es la clase base del tipo de cadena, de hecho, cualquier tipo .Net.

y en el caso cuando T2 es int, es simple boxeo-unboxing.

+0

Hola, Gracias por la respuesta. Creo que todavía es una caja, pero cuando trata de encajonarla, encuentra el tipo como tipo de referencia, por lo que no hace nada como mencionó Thomas Levesque. – RAM

6

El compilador debe generar IL que pueda funcionar en todos los tipos genéricos. El compilador no puede saber que siempre instatáis GCClass con <string, int>. Tiene que hacer frente a la eventualidad de que T1 sea un tipo de valor.

Sin embargo, esperaría que box en un tipo de referencia no sea operativo. El JIT genera diferentes códigos de máquina del IL del método Display para referencia y tipos de valores. Para los tipos de referencia, espero que se elimine la instrucción box.

Si está seguro de que T1 nunca será un tipo de valor, puede agregarle una restricción : class, que eliminará esa instrucción box.

+0

Tim, eso tiene sentido. Gracias por la respuesta. – RAM

4

Mira la CLI specification

en la partición III, sección 4.1, sobre la instrucción box:

Si typeTok es un tipo de valor, la instrucción caja convierte val a su forma en caja. Cuando typeTok es un tipo que no admite nulos (§1.8.2.4), esto se hace por creando un nuevo objeto y copiando los datos de val en el objeto recientemente asignado . Si se trata de un tipo que admite nulo, este se realiza al inspeccionar la propiedad val de HasValue ; si es falso, se inserta una referencia nula en la pila; de lo contrario, el resultado de propiedad del valor del boxing val es empujado a la pila . Si typeTok es un tipo de referencia , la instrucción cuadro no hace nada

Así se produce el boxeo sólo si el parámetro de tipo genérico es en realidad un valor de tipo. Si es un tipo de referencia, esta instrucción no tiene ningún efecto.

+0

Hola Thomas, Gracias por la respuesta. Creo que durante el JIT se decide si se va a boxear o no según el tipo real del operando. ¿Estoy en lo cierto? – RAM

+0

@RAM sí, mira mi respuesta –

Cuestiones relacionadas