2010-01-12 4 views
6

Znalazłem this, ale spróbowałem go użyć i nie udało się.Używanie przedstawiciela do wywoływania konstruktora

Jak mogę utworzyć obiekt za pomocą refleksji i zrobić to szybko, umieszczając go w delegacie?

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

przed wprowadzeniem go w Deleage próbowałem przynajmniej wywołać i kiedy zrobiłem powyżej pojawia się błąd

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

Informacje dodatkowe: wyjątek został rzucony przez cel inwokacji.

jeśli zadzwonię d() zamiast ja dostać wyjątkiem

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

Additional information: Type must derive from Delegate. 

Jak mogę umieścić żadnego konstruktora param do delegata i wywołać ją?

+2

Jaki problem miałeś z Activator.CreateInstance? – dsolimano

+0

dsolimano: Aby zwolnić. Tworzę tysiące obiektów i więcej. –

Odpowiedz

11

Jeśli masz dostęp do .NET 3.5 (który swoimi użycie Func<T> sugeruje), możesz znaleźć Expression łatwiej niż ILGenerator:

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

Całość łatwo jest rozszerzyć, aby użyć określonego konstruktora, przekazując argumenty lub dodając powiązania właściwości po-konstruktorze; rzutowania, konwersje itp. (patrz this related answer). Jeśli masz konkretny scenariusz, z radością dodaję przykład.

Należy również pamiętać, że należy buforować i ponownie używać dowolnych takich konstruktorów - w przeciwnym razie utracisz korzyści (np. Nie odtwarzaj delegata na połączenie).

+0

Doskonała odpowiedź jak zawsze. –

3

Spróbuj tego -

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

Brak argumenty konstruktora więc nie należy załadować argumentów na stosie 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

Ogólna metoda konstruowania delegatów, bezpośrednio wywołanie konstruktora. Automatycznie wyszukuje konstruktor w podanym typie z podpisem o podanym typie delegata i konstruuje delegata tego typu. Kod:

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

jest częścią źródeł projektu Yappi. Za jego pomocą można zbudować delegata wywołującego dowolnego konstruktora danego typu, w tym konstruktora z parametrami (z wyjątkiem parametrów ref i out).

wykorzystanie próbki:

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

Po wybudowaniu delegata, przechowywać go gdzieś w słowniku statycznej lub w statycznym polu klasie rodzajowe parametru. Nie buduj nowego delegata za każdym razem. Użyj jednego uczestnika do tworzenia wielu instancji danego typu.