2010-02-28 3 views
3

동적 메서드를 생성하여 생성자의 대리자 표현을 만들려고합니다.이 메서드는 매우 "느슨하게 형식화 된"서명과 일치해야하므로 모든 매개 변수화 된 형식 생성자 :C# 동적 메서드 대리자 매개 변수화 된 생성자를로드하는 데 문제가 발생합니다.

public delegate Object ParamsConstructorDelegate(params object[] parameters); 

이 대리자를 만드는 코드를 내가지고있어, 이제 (이 실버입니다주의)처럼

public static ParamsConstructorDelegate CreateDelegate(ConstructorInfo constructor) 
    { 
     Guard.ArgumentNotNull(constructor, "constructor"); 
     Guard.ArgumentValue(constructor.GetParameters().Length == 0, MUSTBE_PARAMETERIZED_CONSTRUCTOR); 

     var _argumentTypes = new Type[] { typeof(object[]) }; 
     var _parameters = constructor.GetParameters(); 
     var _parameterTypes = _parameters.Select((p) => p.ParameterType).ToArray(); 

     var _sourceType = constructor.DeclaringType; 
     var _method = new DynamicMethod(constructor.Name, _sourceType, _argumentTypes); 
     var _gen = _method.GetILGenerator(); 

     for (var _i = 0; _i < _parameters.Length; _i++) 
     { 
      if (_parameters[_i].IsOut || _parameterTypes[_i].IsByRef) 
      { 
       if (_i < 128) 
       { 
        _gen.Emit(OpCodes.Ldarga_S, (byte)_i); 
       } 
       else 
        _gen.Emit(OpCodes.Ldarga, _i); 
      } 
      else 
      { 
       switch (_i) 
       { 
        case 0: 
         _gen.Emit(OpCodes.Ldarg_0, _i); 
         break; 
        case 1: 
         _gen.Emit(OpCodes.Ldarg_1, _i); 
         break; 
        case 2: 
         _gen.Emit(OpCodes.Ldarg_2, _i); 
         break; 
        case 3: 
         _gen.Emit(OpCodes.Ldarg_3, _i); 
         break; 
        default: 
         if (_i < 128) 
          _gen.Emit(OpCodes.Ldarg_S, (byte)_i); 
         else 
          _gen.Emit(OpCodes.Ldarg, _i); 
         break; 
       } 
      } 
     } 
     _gen.Emit(OpCodes.Newobj, constructor); 
     _gen.Emit(OpCodes.Ret); ; 

     return (ParamsConstructorDelegate)_method.CreateDelegate(typeof(ParamsConstructorDelegate)); 
    } 

보인다 "작업 런타임을 불안정하게 할 수있다." 확인 예외, 명백하게 일리노이 잘못, 그래서 누군가가 나를 수정할 수 있기를 바랍니다.

감사합니다.

+0

지역 주민을위한 밑줄 접두사는 내가별로 보지 못한 스타일입니다. –

답변

5

두 가지 문제점이 있습니다. 먼저 에서 건까지 암시 적으로 _i이 필요하지 않습니다. 둘째 - 대리인 만 개의 arg (배열)가 있습니다. 배열에서 항목을 가져 와서 캐스팅 할 필요가 있습니다. 아래에있는 것과 같이 (값에 의한 전달 만 처리합니다; ref/out에 대해서는 로컬을 정의하고 stloc/ldloca/etc를 사용해야합니다) : 정보에 대한

using System; 
using System.Reflection.Emit; 
public delegate object ParamsConstructorDelegate(params object[] parameters); 
public class Foo 
{ 
    string s; 
    int i; 
    float? f; 
    public Foo(string s, int i, float? f) 
    { 
     this.s = s; 
     this.i = i; 
     this.f = f; 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
     var ctor = Build(typeof(Foo)); 
     Foo foo1 = (Foo)ctor("abc", 123, null); 
     Foo foo2 = (Foo)ctor(null, 123, 123.45F); 
    } 
    static ParamsConstructorDelegate Build(Type type) 
    { 
     var mthd = new DynamicMethod(".ctor", type, 
      new Type[] { typeof(object[]) }); 
     var il = mthd.GetILGenerator(); 
     var ctor = type.GetConstructors()[0]; // not very robust, but meh... 
     var ctorParams = ctor.GetParameters(); 
     for (int i = 0; i < ctorParams.Length; i++) 
     { 
      il.Emit(OpCodes.Ldarg_0); 
      switch (i) 
      { 
       case 0: il.Emit(OpCodes.Ldc_I4_0); break; 
       case 1: il.Emit(OpCodes.Ldc_I4_1); break; 
       case 2: il.Emit(OpCodes.Ldc_I4_2); break; 
       case 3: il.Emit(OpCodes.Ldc_I4_3); break; 
       case 4: il.Emit(OpCodes.Ldc_I4_4); break; 
       case 5: il.Emit(OpCodes.Ldc_I4_5); break; 
       case 6: il.Emit(OpCodes.Ldc_I4_6); break; 
       case 7: il.Emit(OpCodes.Ldc_I4_7); break; 
       case 8: il.Emit(OpCodes.Ldc_I4_8); break; 
       default: il.Emit(OpCodes.Ldc_I4, i); break; 
      } 
      il.Emit(OpCodes.Ldelem_Ref); 
      Type paramType = ctorParams[i].ParameterType; 
      il.Emit(paramType.IsValueType ? OpCodes.Unbox_Any 
       : OpCodes.Castclass, paramType); 
     } 
     il.Emit(OpCodes.Newobj, ctor); 
     il.Emit(OpCodes.Ret); 
     return (ParamsConstructorDelegate) 
      mthd.CreateDelegate(typeof(ParamsConstructorDelegate)); 
    } 
} 

- 난 게으른 해요 - 나는 IL 나는 C#에서 그것을 쓰기 쓰기 후 반사경에로드 무엇을 알고 싶다면. 사용할 때 나는 그것이 매우 어려운 오류 메시지가 의미 "작업 런타임을 불안정하게 할 수"이해 찾을

static object CreateFoo(object[] vals) 
{ 
    return new Foo((string)vals[0], (int)vals[1], (float?)vals[2]); 
} 

+0

빠른 대답을 주셔서 감사합니다 마크, 배열을 확장하고 값을 생성자로 밀어하는 방법을 가리킬 수 있습니다 - 배열 값을 밖으로 jitsu 및 일리노이 사용하는 방법을 잘 모르겠습니다 때문에. 다시 한번 감사드립니다. – Orktane

+0

@Orktane 업데이트를 보았습니까? –

+0

방금 ​​보았지만 훌륭하게 작동합니다. 게다가 정적 인 생성자를 보여 주셔서 고마워요. 정확히 내가 잃어버린 부분이고, 모두 맞습니다. 도움과 환호를 감사하십시오. – Orktane

1

거기에서 반전 : 예를 들어,이 일을 나는 방법을 썼다 Reflection.Emit - 여기서는 CLR이별로 유용한 정보를 제공하지 않습니다. 문제에 대한 자세한 정보를 얻는 데 사용할 수있는 트릭은 디버그 모드에서 실행할 때 일부 임시 어셈블리 코드 (동적 대리자 방출 이외의 코드)를 내보내도록 코드를 수정하는 것입니다.

> peverify assembly.dll 

당신은 같은 클래스를 사용해야합니다 :

그런 다음 당신은 일반적으로 당신에게 생성 된 IL 코드와 문제에 대한 자세한 정보를 제공, (비주얼 스튜디오 명령 줄에서)을 peverify 도구를 사용할 수 있습니다 AssemblyBuilderModuleBuilder을 사용하여 어셈블리를 제조 하였다. 그런 다음 코어 부분 ( ILGenerator 사용)을 두 번 실행하여 실제 실행을위한 동적 위임과 디버깅을위한 임시 어셈블리를 생성 할 수 있습니다. 나는 peverify가 훨씬 좋은 정보를 제공한다고 믿습니다.

+0

Tomas, 정말 일리노이와 델리게이트 주위에 도움이되는 팁이 있습니다. 왜냐하면 실제로 그 예외는 어딘가에서 엉망진창이라고 말하는 것만큼이나 유용하기 때문입니다. :) 글쎄, 지금은 위의 마크가 나와 있지만 감사합니다. 팁을 위해 많은 것을 명심해야 겠어 .. – Orktane