2009-11-28 24 views
5

Về cơ bản, tôi chấp nhận tên sự kiện dưới dạng chuỗi, để có được EventInfo. Sau đó, tôi khám phá loại trình xử lý sự kiện và loại đối số sự kiện bằng cách sử dụng sự phản chiếu, tạo một đại biểu mới thuộc loại đó (myEventHandler) và kết hợp nó với sự kiện. Khi bao giờ myEventHandler được gọi, tôi cần phải downcast và truyền các đối số cho trình xử lý.IL Phát ra một cuộc gọi đại diện?

Mã của tôi như sau. Trình xử lý 'cần phải được gọi thông qua myEventHandler, khi bao giờ' d 'được gọi. Tôi cần phải có một số mã emit Reflection có nơi tôi đặt ???. Có suy nghĩ gì không?

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

Chỉnh sửa: Dựa trên quan sát qua Reflector.

Các phản xạ tạo ra mã cho một kịch bản tự mã hóa là

.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 
} 

Và đây là những gì tôi đã cố gắng dựa trên đó.

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

Nhưng điều này đang gây ra một lỗi thời gian chạy:

'Calling convention must be varargs'

Có lẽ tôi là thiếu một cái gì đó, cần phải có một cái nhìn tốt hơn vào IL.

+2

Bí quyết ở đây luôn đơn giản là viết mã bạn muốn trong C# và sử dụng bộ phản xạ/ILDASM để xem IL. Tôi sẽ đoán một sự kết hợp của ld, castclass, và callvirt –

+0

Yep đồng ý. Tôi sẽ đi theo con đường đó, nhưng nghĩ rằng bất kỳ Reflection nào phát ra Ninjas trong SO có thể nhanh chóng chỉ ra điều này – amazedsaint

+0

Nhìn lại - nơi nào "handler" cư trú? liên quan đến args? Tôi nghĩ nó sẽ là một nỗi đau khi đưa hai người lại gần nhau. Có vẻ như phiên bản C# sử dụng một lớp chụp, nhưng phương pháp động của bạn ở phút là tĩnh, vì vậy không nơi nào để xô bất kỳ trạng thái nào ... –

Trả lời

5

Hóa ra tôi là những thứ quá phức tạp! 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 
} 

Điều đó phù hợp với các trường hợp thử nghiệm của tôi.

5

OK - điều này có thể hữu ích; nó tạo ra IL để chuyển đổi giữa các loại đại biểu, miễn là chúng khớp với mẫu chuẩn. Nó chỉ thêm một castclass khi cần thiết (vì vậy nếu bạn đang đi từ một MouseEventArgs đến một EventArgs nó không phải là cần thiết, nhưng theo hướng ngược lại nó là). Vì bạn rõ ràng đang làm việc với sự phản chiếu, tôi đã không sử dụng Generics (điều này sẽ làm cho mọi việc trở nên khó khăn hơn).

Bit táo bạo thay vì sử dụng lớp học chụp, nó giả vờ phương pháp thuộc về dữ liệu tôi sẽ nắm bắt và sử dụng trạng thái là arg0. Tôi không thể quyết định điều đó làm cho nó xấu xa hay thông minh, vì vậy tôi sẽ đi với "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); 
    } 
} 

Rõ ràng bạn vẫn cần phải tạo ra một đại biểu cho trường hợp sử dụng phương pháp thông thường (Delegate.CreateDelegate vv), nhưng sau đó bạn có thể bọc nó vào một EventHandler, hoặc ngược lại.

+0

Tuyệt vời. Vẫn cần phải thử điều này, nhưng tôi không chắc liệu tôi có thể có câu trả lời hay hơn không. Do đó, chấp nhận điều này như một câu trả lời. Cảm ơn :) – amazedsaint

+0

Vâng, đã xác nhận. Giải quyết những gì tôi muốn. – amazedsaint

Các vấn đề liên quan