2009-11-28 3 views
5

기본적으로 이벤트 이름을 문자열로 받아 들여 EventInfo이됩니다. 그런 다음 리플렉션을 사용하여 이벤트 핸들러 유형 및 이벤트 인수 유형을 찾고 해당 유형의 새 델리게이트 (myEventHandler)를 작성한 후 이벤트와 연결합니다. 언제 myEventHandler 호출 될 때, 나는 다운 캐스트하고 처리기에 인수를 전달해야합니다.IL Delegate 인스턴스를 호출하기위한 Emit?

내 코드는 다음과 같습니다. '핸들러'는 myEventHandler을 통해 호출되어야하며 'd'가 호출됩니다. 나는 반사 (Reflection) 코드를 거기에 놓을 필요가있다. 이견있는 사람?

EventHandler handler = delegate(object sender, EventArgs eventArgs) 
{ 
    //something will happen here         
}; 

Type[] typeArgs = { typeof(object), derivedEventArgsType }; 

DynamicMethod myEventHandler = new DynamicMethod("", typeof(void), typeArgs); 
var ilgen = myEventHandler.GetILGenerator(); 

//What should be the IL code here to 
//cast derviedEventArgs to EventArgs and 
//invoke the 'handler' above?????? 
ilgen.Emit(OpCodes.Pop); 
ilgen.Emit(OpCodes.Ret); 



Delegate d = dynamic.CreateDelegate(derviedEventHandlerType); 

//addMethod is the add MethodInfo for an Event 
addMethod.Invoke(target, new object[] { d }); 

편집 : 반사경을 통해 관찰을 기준으로합니다.

반사기는 수동으로 코딩 시나리오에 대한 코드를 생성

.method public hidebysig instance void <Main>b__1(object sender, class ConsoleApplication2.MyEventArgs e) cil managed 
{ 
    .maxstack 8 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld class [mscorlib]System.EventHandler ConsoleApplication2.Program/<>c__DisplayClass3::handler 
    L_0007: ldarg.1 
    L_0008: ldarg.2 
    L_0009: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, class [mscorlib]System.EventArgs) 
    L_000e: nop 
    L_000f: ret 
} 

입니다 그리고 이것은 내가 그 기반으로 시도하는 것이다.

ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ldarg_0); 
ilgen.Emit(OpCodes.Ldfld,eh.GetType().GetField("handler")); 
ilgen.Emit(OpCodes.Ldarg_1); 
ilgen.Emit(OpCodes.Ldarg_2); 
ilgen.EmitCall(OpCodes.Callvirt,eh.handler.Method, 
       new Type[]{ typeof(object), typeof(EventArgs) }); 
ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ret); 

그러나이 런타임 오류를 일으키는 :

'Calling convention must be varargs'

아마 내가 부족 뭔가, IL에 더 나은 모습이 필요합니다.

+2

트릭은 항상 C#에서 원하는 코드를 작성하고 reflector/ILDASM을 사용하여 IL을 봅니다. 나는 ld, castclass, 그리고 callvirt의 조합을 추측 할 것이다 –

+0

그래도 동의했다. 나는 그 길을 택할 것이지만 어떤 반사도 생각해 냈다. 그래서 Ninjas는 빠르게 이것을 지적 할 수있다. – amazedsaint

+0

"Handler"는 어디에 있는가? args와 관련하여? 저는 두 사람을 데려 오는 것이 고통 스러울 것이라고 생각하고 있습니다. C# 버전에서 캡처 클래스를 사용하는 것처럼 보입니다. 그러나 분당 동적 메서드는 정적이므로 아무 곳이나 밀어 넣지 않아도됩니다. –

답변

5

나는 크게 복잡하게되어 버렸다. Barry Kelly had the right idea :

static T CastDelegate<T>(Delegate src) 
    where T : class 
{ 
    return (T)(object)Delegate.CreateDelegate(
     typeof(T), 
     src.Target, 
     src.Method, 
     true); // throw on fail 
} 

내 테스트 사례에 해당합니다.

5

확인 - 도움이 될 수 있습니다. 대리자 형식을 표준 패턴과 일치하는 한 IL을 생성하여 대리자 형식간에 전환합니다. 필요한 경우에만 캐스트 클래스를 추가합니다 (따라서 MouseEventArgs에서 EventArgs으로 이동하는 경우에는 필요하지 않지만 역순으로 이동합니다). 당신이 명확하게 반사 작용을하고 있기 때문에 제네릭을 사용하지 않았습니다.

건방진 비트는 캡쳐 클래스를 사용하는 대신 메서드가 캡처 할 데이터에 속해있는 것으로 가장하며 상태를 arg0으로 사용합니다. 그것이 그것이 악하거나 영리하게 만들면 나는 결정할 수 없다. 그래서 나는 "clevil"과 함께 갈 것이다.

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Threading; 
using System.Windows.Forms; 

class Program { 
    static ParameterInfo[] VerifyStandardHandler(Type type) { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (!typeof(Delegate).IsAssignableFrom(type)) throw new InvalidOperationException(); 
     MethodInfo sig = type.GetMethod("Invoke"); 
     if (sig.ReturnType != typeof(void)) throw new InvalidOperationException(); 
     ParameterInfo[] args = sig.GetParameters(); 
     if (args.Length != 2 || args[0].ParameterType != typeof(object)) throw new InvalidOperationException(); 
     if (!typeof(EventArgs).IsAssignableFrom(args[1].ParameterType)) throw new InvalidOperationException(); 
     return args; 
    } 
    static int methodIndex; 
    static Delegate Wrap(Delegate value, Type type) { 
     ParameterInfo[] destArgs = VerifyStandardHandler(type); 
     if (value == null) return null; // trivial 
     if (value.GetType() == type) return value; // already OK 
     ParameterInfo[] sourceArgs = VerifyStandardHandler(value.GetType()); 
     string name = "_wrap" + Interlocked.Increment(ref methodIndex); 
     Type[] paramTypes = new Type[destArgs.Length + 1]; 
     paramTypes[0] = value.GetType(); 
     for (int i = 0; i < destArgs.Length; i++) { 
      paramTypes[i + 1] = destArgs[i].ParameterType; 
     } 
     DynamicMethod dyn = new DynamicMethod(name, null, paramTypes); 
     MethodInfo invoker = paramTypes[0].GetMethod("Invoke"); 
     ILGenerator il = dyn.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_2); 
     if (!sourceArgs[1].ParameterType.IsAssignableFrom(destArgs[1].ParameterType)) { 
      il.Emit(OpCodes.Castclass, sourceArgs[1].ParameterType); 
     } 
     il.Emit(OpCodes.Call, invoker); 
     il.Emit(OpCodes.Ret); 
     return dyn.CreateDelegate(type, value); 
    } 
    static void Main() { 
     EventHandler handler = delegate(object sender, EventArgs eventArgs) { 
      Console.WriteLine(eventArgs.GetType().Name); 
     }; 
     MouseEventHandler wrapper = (MouseEventHandler)Wrap(handler, typeof(MouseEventHandler)); 
     MouseEventArgs ma = new MouseEventArgs(MouseButtons.Left, 1, 1, 1, 1); 
     wrapper(new object(), ma); 

     EventHandler backAgain = (EventHandler)Wrap(wrapper, typeof(EventHandler)); 
     backAgain(new object(), ma); 
    } 
} 

은 분명히 당신은 여전히 ​​일정한 방법을 사용하여 이벤트 (Delegate.CreateDelegate 등)에 대리자를 생성 할 필요가 있지만 다음 EventHandler, 또는 역으로 포장 할 수 있습니다.

+0

굉장합니다. 여전히이 문제를 해결해야하지만 더 나은 답변을 얻을 수 있는지 여부는 확실하지 않습니다. 따라서 이것을 대답으로 받아들입니다. 감사합니다 :) – amazedsaint

+0

음, 확인되었습니다. 내가 원하는 것을 해결했다. – amazedsaint