2012-11-20 3 views
4

임의의 생성자 매개 변수를 사용하여 클래스의 인스턴스를 생성하려면 Reflection Emit을 사용하고 싶습니다. 이것은 내 코드와 같은 모습입니다 :Reflection 클래스 인스턴스를 생성하기 위해 방출합니다.

public delegate object ObjectActivator(params object[] args); 
static void Main(string[] args) 
{ 
    var ao = new { ID = 10000, FName = "Sample", SName = "Name" }; 
    var t = ao.GetType(); 
    var info = t.GetConstructor(new Type[] { typeof(int), typeof(string), typeof(string) }); 
    var objActivatorEmit = GetActivatorEmit(info); 
    var obj = createdActivatorEmit(4, "Foo", "Bar"); 
} 
public static ObjectActivator GetActivatorEmit(ConstructorInfo ctor) 
{ 
    ParameterInfo[] paramsInfo = ctor.GetParameters(); 
    DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) }); 
    ILGenerator gen = method.GetILGenerator(); 
    for (int i = 0; i < paramsInfo.Length; i++) 
    { 
     Type t = paramsInfo[i].ParameterType; 
     gen.Emit(OpCodes.Ldarg_0); // Push array (method argument) 
     gen.Emit(OpCodes.Ldc_I4, i); // Push i 
     gen.Emit(OpCodes.Ldelem_Ref); // Pop array and i and push array[i] 
     if(t.IsValueType) 
     { 
      gen.Emit(OpCodes.Unbox_Any, t); // Cast to Type t 
     } 
     else 
     { 
      gen.Emit(OpCodes.Castclass, t); //Cast to Type t 
     } 
    } 
    gen.Emit(OpCodes.Newobj, ctor); 
    gen.Emit(OpCodes.Ret); 
    return (ObjectActivator)method.CreateDelegate(typeof(ObjectActivator)); 
} 

코드는 오류 메시지 Attempt by method 'DynamicClass.CreateInstance(System.Object[])' to access method '<>f__AnonymousType1'3<System.Int32,System.__Canon,System.__Canon>..ctor(Int32, System.__Canon, System.__Canon)' failed.MethodAccessException 실패합니다.

무엇이 잘못 되었나요?

답변

3

에서 오류 메시지는 익명 형식의 생성자는 공공 없음을 나타냅니다. 나는 익명 형식의 생성자는 항상 internal 생각, 그래서 당신은 다른 DynamicMethod 생성자를 사용하여 가시성 검사를 생략해야합니다 :이 부분 신뢰 시나리오에서 작동하지 않는 것이

DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) }, true); 

참고.

+0

나는 그것이 일반 대중 ctor라고 생각합니다. http://blogs.msdn.com/b/ericlippert/archive/2010/12/20/why-are-anonymous-types-generic.aspx – Elisha

+0

@Elisha - 내부 형식의 공용 생성자이므로 여전히 액세스 할 수 없습니다. – kvb

+0

은 현재 정상적으로 작동합니다. – user1622959

2

Reflection.Emit을 사용할 필요가 없으므로 권장하지 않습니다. 자신이하는 일을 알고 있거나 다른 API로는 충족시킬 수없는 특별한 필요를 느끼지 않는다면 멀리 떨어져있는 것이 가장 좋습니다.

오른쪽에 오기가 훨씬 쉬운 세 가지 대안이 있습니다. 그것을 체크 아웃 :

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

public static class App 
{ 
    public delegate object ObjectActivator(params object[] args); 

    public static void Main(string[] args) 
    { 
     var ao = new { ID = 10000, FName = "Sample", SName = "Name" }; 
     var t = ao.GetType(); 
     var info = t.GetConstructor(new[] { typeof(int), typeof(string), typeof(string) }); 
     if (info == null) 
     { 
      throw new Exception("Info is null"); 
     } 

     // This uses System.Linq.Expressions to create the delegate 
     var activator = GetActivator(info); 
     var newObj1 = activator(4, "Foo", "Bar"); 

     // This invokes the ConstructorInfo directly 
     var newObj2 = info.Invoke(new object[] { 4, "Foo", "Bar" }); 

     // This uses System.Activator to dynamically create the instance 
     var newObj3 = Activator.CreateInstance(t, 4, "Foo", "Bar"); 
    } 

    // This uses System.Linq.Expressions to generate a delegate 
    public static ObjectActivator GetActivator(ConstructorInfo ctor) 
    { 
     var args = Expression.Parameter(typeof(object[]), "args"); 
     var parameters = ctor.GetParameters().Select((parameter, index) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(index)), parameter.ParameterType)); 
     return Expression.Lambda<ObjectActivator>(Expression.New(ctor, parameters), args).Compile(); 
    } 
} 

참고 : 영감을 GetActivator 방법에 대한 this post

+1

감사합니다.하지만 모든 옵션을 테스트하기 위해 마이크로 벤치 마크를 작성 중이 었는데 Emit이 작동하지 않는 유일한 방법이었습니다. – user1622959

+0

ILGenerator와의 작업에 어려움을 겪고있었습니다. 'Expression'을 사용하는 것이 훨씬 쉬웠습니다. 위임자를 호출하기 위해 생성자 호출을 캐시하는 데 사용했습니다. 고마워요. –