2011-07-15 9 views
14

Si tengo un parámetro de restricción de tipo new():Dado "donde T: new()", hace "new T()" usar Activator.CreateInstance internamente?

void Foo<T>() where T : new() 
{ 
    var t = new T(); 
} 

¿Es cierto que new T() utilizará internamente la Activator.CreateInstance método (es decir, la reflexión)?

+2

¿Importa? – Oded

+6

@Oded Si está intentando comprender mejor el entorno en el que está programando, entonces sí, ¡sí importa! :-) – Justin

+1

@Kragen: ildasm, monodis – sehe

Respuesta

11

Sí, esto es cierto. Editar 2: Aquí hay una buena explicación del cómo y por qué.

http://www.simple-talk.com/community/blogs/simonc/archive/2010/11/17/95700.aspx

Para la verificación que compiló el siguiente método:

public static T Create<T>() where T: new() { 
    return new T(); 
} 

Y esta es la IL generada cuando se compila con el compilador de C# en .NET 3.5 SP1:

.method public hidebysig static !!T Create<.ctor T>() cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] !!T local, 
     [1] !!T local2) 
    L_0000: ldloca.s local 
    L_0002: initobj !!T 
    L_0008: ldloc.0 
    L_0009: box !!T 
    L_000e: brfalse.s L_001a 
    L_0010: ldloca.s local2 
    L_0012: initobj !!T 
    L_0018: ldloc.1 
    L_0019: ret 
    L_001a: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>() 
    L_001f: ret 
} 

Editar: El compilador C# 4 crea código ligeramente diferente, pero similar,

.method public hidebysig static !!T Create<.ctor T>() cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] !!T CS$1$0000, 
     [1] !!T CS$0$0001) 
    L_0000: nop 
    L_0001: ldloca.s CS$0$0001 
    L_0003: initobj !!T 
    L_0009: ldloc.1 
    L_000a: box !!T 
    L_000f: brfalse.s L_001c 
    L_0011: ldloca.s CS$0$0001 
    L_0013: initobj !!T 
    L_0019: ldloc.1 
    L_001a: br.s L_0021 
    L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>() 
    L_0021: stloc.0 
    L_0022: br.s L_0024 
    L_0024: ldloc.0 
    L_0025: ret 
} 

En el caso de un tipo de valor que no utiliza el activador pero sólo devuelve el valor default(T), de lo contrario, invoca el método Activator.CreateInstance.

+0

@Jason: 'initobj' se define como" Inicializa cada campo del tipo de valor en una dirección especificada en una referencia nula o un 0 del tipo primitivo apropiado. ... A diferencia de Newobj, initobj no llama al método constructor. está destinado a inicializar tipos de valores, mientras que newobj se usa para asignar e inicializar objetos. " No hay ninguna indicación de que llame al constructor predeterminado de un tipo de referencia.Supongo que es equivalente a 'default (T)' – CodesInChaos

+0

@CodeInChaos: Ya eliminé mi comentario. Cometí un error. – jason

+1

Bien, entonces la gran pregunta es por qué. ¿Por qué el compilador no está emitiendo una instrucción 'newobj'? – jason

1

Sí. Lo hace para los tipos de referencia.

Usando ILSpy en el siguiente código de liberación-compilado:

public static void DoWork<T>() where T: new() 
    { 
     T t = new T(); 
     Console.WriteLine(t.ToString()); 
    } 

cedido

.method public hidebysig 
    instance void DoWork<.ctor T>() cil managed 
{ 
    // Method begins at RVA 0x2064 
    // Code size 52 (0x34) 
    .maxstack 2 
    .locals init (
     [0] !!T t, 
     [1] !!T CS$0$0000, 
     [2] !!T CS$0$0001 
    ) 

    IL_0000: ldloca.s CS$0$0000 
    IL_0002: initobj !!T 
    IL_0008: ldloc.1 
    IL_0009: box !!T 
    IL_000e: brfalse.s IL_001b 

    IL_0010: ldloca.s CS$0$0001 
    IL_0012: initobj !!T 
    IL_0018: ldloc.2 
    IL_0019: br.s IL_0020 

    IL_001b: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>() 

    IL_0020: stloc.0 
    IL_0021: ldloca.s t 
    IL_0023: constrained. !!T 
    IL_0029: callvirt instance string [mscorlib]System.Object::ToString() 
    IL_002e: call void [mscorlib]System.Console::WriteLine(string) 
    IL_0033: ret 
} // end of method Program::DoWork 

O en C#:

public void DoWork<T>() where T : new() 
{ 
    T t = (default(T) == null) ? Activator.CreateInstance<T>() : default(T); 
    Console.WriteLine(t.ToString()); 
} 

JIT creará diferentes instrucciones compiladas para cada valor diferente tipo parámetro pasado, pero utilizará las mismas instrucciones para los tipos de referencia - he nce el Activador.CrearInstancia()

+1

Verdad para "Value Types" también . 'Ver página 544/Capítulo 11 del libro -El lenguaje de programación C# - 3ra edición'. –

Cuestiones relacionadas