IL Emit для вызова экземпляра делегата? - PullRequest
6 голосов
/ 28 ноября 2009

По сути, я принимаю имя события в виде строки, чтобы получить EventInfo. Затем я обнаруживаю тип обработчика события и тип аргумента события, используя отражение, создавая новый делегат этого типа (myEventHandler) и подключая его к событию. Когда когда-либо myEventHandler вызывается, мне нужно уменьшить и передать аргументы обработчику.

Мой код, как показано ниже. «Обработчик» должен вызываться через 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);

Но это вызывает ошибку времени выполнения:

«Соглашение о вызовах должно быть varargs»

Возможно, я что-то упустил, мне нужно лучше изучить ИЛ.

Ответы [ 2 ]

5 голосов
/ 30 ноября 2009

Оказывается, я сильно усложнял вещи! Барри Келли имел правильную идею :

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 голосов
/ 29 ноября 2009

ОК - это может помочь; он генерирует IL для переключения между типами делегатов, если они соответствуют стандартному шаблону. Он добавляет литой класс только в случае необходимости (поэтому, если вы переходите от MouseEventArgs к EventArgs, это не обязательно, но в обратном направлении это так). Поскольку вы явно работаете с рефлексией, я не использовал дженерики (что усложнит ситуацию).

Нахальный бит в том, что вместо использования класса capture он делает вид, что метод относится к данным, которые я собираю, и использует состояние как arg0. Я не могу решить, делает ли это это злом или умным, поэтому я пойду с "Кливил".

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 или наоборот.

...