2009-11-28 23 views
5

Temel olarak, EventInfo almak için bir olay adını bir dize olarak kabul ediyorum. Sonra yansıma kullanarak olay işleyicisi türünü ve olay bağımsız değişken türünü keşfediyorum, bu türden yeni bir temsilci (myEventHandler) oluşturmak ve olayla bağlantı kurmak. myEventHandler çağrısı yapıldığında, argümanları işleyiciye indirmem ve iletmem gerekiyor.IL Temsilci örneğini çağırmak için IL Emit?

Kodum aşağıdaki gibidir. 'İşleyici', 'd' çağrısı yapıldığında myEventHandler aracılığıyla çağrılmalıdır. Orada koyduğum bazı Yansıma yayma kodlarına sahip olmam gerekiyor ???. Düşüncesi olan var mı?

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

Düzenleme: Reflektör yoluyla gözlemlere dayanarak.

reflektör elle kodlanmış senaryo için kod oluşturulan

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

Ve bu bunu dayanarak çalıştığı şey budur.

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

Ama bu çalışma zamanı hatası neden oluyor:

'Calling convention must be varargs'

Muhtemelen ben eksik bir şey, IL içine iyice aramak gerekir.

+2

Burada hile yapmak istediğiniz her zaman C# içinde istediğiniz kodu yazmak ve IL'ye bakmak için reflektör/ILDASM kullanmanız yeterlidir. Anlaşılan Ld, castclass ve callvirt –

+0

Yep'in bir kombinasyonunu tahmin ediyorum. Bu rotayı alacağım ama herhangi bir Reflection'un, Ninjas'ı SO içinde gönderebileceğini düşündüğünüzde, bunu hızlı bir şekilde belirtebiliriz. – amazedsaint

+0

Tekrar bakın - "işleyici" nerede oturuyor? silahlara göre? İkisini bir araya getirmek için bir acı olacağını düşünüyorum. C# versiyonu bir yakalama sınıfı kullanıyor gibi görünüyor, ama dakikadaki dinamik yönteminiz durağan, bu yüzden herhangi bir devlete hiçbir şey yapamazsınız ... –

cevap

5

Oldukça karmaşık bir şeydi. 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 
} 

benim test durumları için çalışır.

5

Tamam - bu yardımcı olabilir; Standart kalıba uydukları sürece delege türleri arasında geçiş yapmak için IL üretir. Yalnızca gerektiğinde bir castclass ekler (bu nedenle MouseEventArgs'dan EventArgs'a geçmeniz gerekli değildir, ancak tersi yönde). Açıkça yansımayla çalıştığınız için, jenerikler kullanmıyorum (ki bu da işleri daha da zorlaştıracaktır).

yaramaz biraz yerine yakalama sınıfı kullanarak, bu yöntem, I gibi yakalamak verilere ait davrandığı ve arg0 olarak durumunu kullanmasıdır. Bunun kötülük mü yoksa akıllı mı olduğuna karar veremem, bu yüzden "clevil" ile gideceğim.

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

Açıkçası Eğer normal yöntemlerle olay (Delegate.CreateDelegate vs) için bir delege oluşturmak gerekir, ancak daha sonra bir EventHandler veya bir geri dönüşe karşı sarın.

+0

Awesome. Hala bunu denemeliyim, ama daha iyi bir cevap alıp alamayacağımı bilmiyorum. Bu nedenle, bunu bir cevap olarak kabul etmek. Teşekkürler :) – amazedsaint

+0

Doğrulandı. İstediğimi çözdüm. – amazedsaint

İlgili konular