2010-01-12 10 views
6

J'ai trouvé this mais j'ai essayé de l'utiliser et j'ai échoué.Utilisation d'un délégué pour appeler un constructeur

Comment puis-je créer un objet en utilisant des réflexions et le rendre rapide en le mettant dans un délégué?

 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[] { }); 

Avant de le mettre dans un Deleage j'ai essayé au moins invoquer et quand je l'ai fait ci-dessus je reçois l'erreur

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

Informations complémentaires: Exception a été levée par la cible d'un appel.

Si j'appelle d() au lieu que je reçois l'exception

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

Additional information: Type must derive from Delegate. 

Comment puis-je mettre un pas de constructeur de délégué dans un param et l'appeler?

+2

Quel problème aviez-vous avec Activator.CreateInstance? – dsolimano

+0

dsolimano: Pour ralentir. Je crée des milliers d'objets et plus encore. –

Répondre

11

Si vous avez accès à .NET 3.5 (que votre utilisation de Func<T> suggère), vous pouvez trouver plus facile 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();   
} 

Assez facile à étendre pour utiliser un constructeur spécifique, transmettre des arguments ou ajouter des liaisons de propriétés post-constructeur; conversions, etc. (voir this related answer). Si vous avez un scénario spécifique, je vais ajouter un exemple avec plaisir. Notez également que vous devez mettre en cache et réutiliser ces constructeurs, sinon vous perdrez le bénéfice (c'est-à-dire que vous ne recréez pas le délégué par appel).

+0

Excellente réponse comme toujours. –

3

Essayez ceci -

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

Il n'y a pas d'arguments au constructeur de sorte que vous ne devriez pas charger des arguments sur la pile 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éthode générique pour construire des délégués, appeler le constructeur directement. Recherche automatiquement le constructeur dans un type donné avec la signature du type de délégué donné et construit le délégué de ce type. Code ici:

/// <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); 
    } 
} 

fait partie des sources de Yappi projet. En l'utilisant, vous pouvez construire un délégué appelant n'importe quel constructeur de type donné, y compris un constructeur avec des paramètres (sauf les paramètres ref et out).

Exemple d'utilisation:

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

Après la construction du délégué, stocker quelque part dans le dictionnaire statique ou dans le champ statique de classe avec le paramètre générique. Ne construisez pas de nouveau délégué à chaque fois. Utilisez un délégué pour construire plusieurs instances de type donné.