2010-01-12 22 views
6

Encontré this pero intenté usarlo y fallé.Usando un delegado para llamar a un constructor

¿Cómo puedo crear un objeto utilizando reflexiones y hacerlo rápido al ponerlo en un delegado?

 DynamicMethod dm = new DynamicMethod("MyCtor", t, new Type[] { });    
     var ctor = t.GetConstructor(new Type[] { }); 
     ILGenerator ilgen = dm.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldarg_0); 
     ilgen.Emit(OpCodes.Newobj, ctor); 
     ilgen.Emit(OpCodes.Ret); 
     var d = (Func<T>)dm.CreateDelegate(t); 
     dm.Invoke(null, new object[] { }); 

Antes de ponerlo en una Deleage he intentado al menos invocarlo y cuando lo hice anteriormente me sale el error

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll 

Información adicional: Se inició una excepción por el destino de una invocación.

si llamo d() en su lugar me da la excepción

An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll 

Additional information: Type must derive from Delegate. 

¿Cómo pongo un parámetro ningún constructor en un delegado y lo llaman?

+2

¿Qué problema tenías con Activator.CreateInstance? – dsolimano

+0

dsolimano: Para disminuir la velocidad. Estoy creando miles de objetos y más. –

Respuesta

11

Si usted tiene acceso a .NET 3.5 (que a su uso de Func<T> sugiere), se puede encontrar más fácil que ExpressionILGenerator:

class Foo { } 
static void Main() { 
    Func<Foo> func = GetCtor<Foo>(); // cache this somewhere! 
    Foo foo = func(); 
} 
static Func<T> GetCtor<T>() { 
    Type type = typeof(T); 
    Expression body = Expression.New(type); 
    return Expression.Lambda<Func<T>>(body).Compile();   
} 

Bastante fácil de extender para usar un constructor específico, pasar argumentos o agregar enlaces de propiedades posteriores al constructor; moldes, conversiones, etc. (ver this related answer). Si tienes un escenario específico, felizmente agregaré un ejemplo.

Tenga en cuenta también que debe almacenar en caché y reutilizar cualquiera de dichos constructores; de lo contrario, perderá el beneficio (es decir, no volverá a crear el delegado por llamada).

+0

Excelente respuesta como siempre. –

3

Prueba esto -

Action myCtor = CreateCtor(t, Type.EmptyTypes, typeof(Action)); 

public static Delegate CreateCtor(Type type, Type[] parameterTypes, Type delegateType, string typeParameterName) 
{ 
    var ctorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, parameterTypes, null); 
    if (ctorInfo == null) 
    { 
     string parameterString = string.Empty; 
     if(parameterTypes.Length > 0) 
     { 
      string[] parameterStrings = new string[parameterTypes.Length]; 
      for(int i = 0; i < parameterTypes.Length; ++i) 
      { 
       parameterStrings[i] = parameterTypes[i].ToString(); 
      } 
      parameterString = string.Join(",", parameterStrings); 
     } 
     throw new ArgumentException(string.Format("Type '{0}' does not define .ctor({1}).", type, parameterString), typeParameterName); 
    } 

    bool isVisible = type.IsVisible && (ctorInfo.IsPublic && !ctorInfo.IsFamilyOrAssembly); 

    DynamicMethod dynamicCtor = new DynamicMethod(Guid.NewGuid().ToString("N"), type, parameterTypes, ctorInfo.Module, !isVisible); 
    var il = dynamicCtor.GetILGenerator(); 
    for (int i = 0; i < parameterTypes.Length; ++i) 
    { 
     switch (i) 
     { 
      case 0: il.Emit(OpCodes.Ldarg_0); break; 
      case 1: il.Emit(OpCodes.Ldarg_1); break; 
      case 2: il.Emit(OpCodes.Ldarg_2); break; 
      case 3: il.Emit(OpCodes.Ldarg_3); break; 
      default: il.Emit(OpCodes.Ldarg, i); break; 
     } 
    } 
    il.Emit(OpCodes.Newobj, ctorInfo); 
    il.Emit(OpCodes.Ret); 
    return dynamicCtor.CreateDelegate(delegateType); 
} 
0

No hay argumentos al constructor lo que no debe cargar argumentos en la pila ilgen.Emit(OpCodes.Ldarg_0):

class Program 
{ 
    static void Main() 
    { 
     var t = typeof(Program); 
     var dm = new DynamicMethod("MyCtor", t, new Type[0], t.Module); 
     var ctor = t.GetConstructor(new Type[0]); 
     ILGenerator ilgen = dm.GetILGenerator(); 
     ilgen.Emit(OpCodes.Newobj, ctor); 
     ilgen.Emit(OpCodes.Ret); 
     var del = (Func<Program>)dm.CreateDelegate(typeof(Func<Program>)); 
     var instance = del(); 
     Console.WriteLine(instance); 
    } 
} 
0

Método genérico para construir delegados, llamando al constructor directamente. Busca automáticamente el constructor en un tipo dado con la firma del tipo de delegado y crea un delegado de ese tipo. Código aquí:

/// <summary> 
/// Reflective object construction helper. 
/// All methods are thread safe. 
/// </summary> 
public static class Constructor 
{ 
    /// <summary> 
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType. 
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType. 
    /// </summary> 
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param> 
    /// <param name="instanceType">Type of instance to be constructed.</param> 
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns> 
    public static Delegate Compile(Type delegateType,Type instanceType) 
    { 
     if (!typeof(Delegate).IsAssignableFrom(delegateType)) 
     { 
      throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType"); 
     } 
     var invoke = delegateType.GetMethod("Invoke"); 
     var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray(); 
     var resultType = invoke.ReturnType; 
     if(!resultType.IsAssignableFrom(instanceType)) 
     { 
      throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName)); 
     } 
     var ctor = instanceType.GetConstructor(
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); 
     if(ctor == null) 
     { 
      throw new ArgumentException("Can't find constructor with delegate's signature","instanceType"); 
     } 
     var parapeters = parameterTypes.Select(Expression.Parameter).ToArray(); 

     var newExpression = Expression.Lambda(delegateType, 
      Expression.Convert(Expression.New(ctor, parapeters), resultType), 
      parapeters); 
     var @delegate = newExpression.Compile(); 
     return @delegate; 
    } 
    public static TDelegate Compile<TDelegate>(Type instanceType) 
    { 
     return (TDelegate) (object) Compile(typeof (TDelegate), instanceType); 
    } 
} 

es parte de las fuentes de Yappi del proyecto. Utilizándolo, puede construir un delegado llamando a cualquier constructor de un tipo determinado, incluido el constructor con parámetros (excepto los parámetros ref y out).

Ejemplo de uso:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>)); 
var list = newList(100); 

Después de la construcción del delegado, almacenarlo en algún lugar en el diccionario estático o en el campo estático de la clase con el parámetro genérico. No construyas un nuevo delegado cada vez. Use un delegado para construir múltiples instancias de tipo dado.

Cuestiones relacionadas