2010-01-12 1 views
6

this을 찾았지만 사용하려고 시도했지만 실패했습니다.대리자를 사용하여 생성자를 호출하려면

어떻게하면 반사를 사용하여 개체를 만들고 대리인에 넣어서 빠르게 만들 수 있습니까?

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

deleage에 넣어 전에 나는 적어도를 호출하기 위해 노력하고 내가 이상했을 때 나는 오류를

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

추가 정보를 얻을 : 호출 대상이 예외가 throw되었습니다. 내가 D를 호출하면

() 대신 내가

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

Additional information: Type must derive from Delegate. 

이 어떻게 대리인에 더 PARAM 생성자를 두지 않는 예외를 얻고 호출?

+2

Activator.CreateInstance에서 어떤 문제점이 있습니까? – dsolimano

+0

dsolimano : 천천히. 나는 수천 개의 물건과 그 이상을 창조하고 있습니다. –

답변

11

당신이 .NET 3.5에 액세스 할 수있는 경우 (이 당신을 Func<T>의 사용을 제안 함) 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();   
} 

특정 생성자를 사용하거나 인수를 전달하거나 사후 생성자 속성 바인딩을 추가하기가 상당히 쉽습니다. 캐스트, 전환 등 (this related answer 참조). 특정 시나리오가 있다면, 나는 예제를 기꺼이 추가 할 것이다.

이러한 생성자를 캐시하고 다시 사용해야합니다. 그렇지 않으면 이점이 손실됩니다 (즉, 통화 당 대표자를 다시 작성하지 않음).

+0

항상 훌륭한 답변. –

3

이 시도 -

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

을 생성자에 인수가 없습니다 당신이 스택 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

생성자를 직접 호출하고 생성자를 직접 호출하는 일반 메서드입니다. 지정된 형식의 생성자를 지정된 대리자 형식의 서명으로 자동 검색하고 해당 형식의 대리자를 만듭니다. 코드는 여기에 있습니다 :

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

Yappi 프로젝트 소스의 일부입니다. 이를 사용하여 매개 변수가있는 생성자 (ref 및 out 매개 변수 제외)를 포함하여 지정된 유형의 모든 생성자를 호출하는 대리자를 생성 할 수 있습니다.

샘플 사용 :

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

대리인의 건설 후, 어딘가 정적 사전에 또는 일반 매개 변수를 사용하여 클래스의 정적 필드에 저장합니다. 매번 새로운 대리인을 구성하지 마십시오. 주어진 유형의 여러 인스턴스를 구성하는 데 하나의 대리자를 사용하십시오.