IL Emit for invoking a delegate instance?

 ̄綄美尐妖づ 提交于 2019-12-01 04:58:51

It turns out I was vastly over-complicating things! 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
}

That works for my test cases.

OK - this might help; it generates the IL to switch between delegate types, as long as they match the standard pattern. It adds a castclass only when necessary (so if you are going from a MouseEventArgs to an EventArgs it isn't necessary, but in the reverse direction it is). Since you are clearly working with reflection, I haven't used generics (which would make things harder).

The cheeky bit is that instead of using a capture class, it pretends the method belongs to the data I would capture, and uses the state as arg0. I can't decide if that makes it evil or clever, so I'll go with "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);
    }
}

Obviously you still need to generate a delegate to the event using regular methods (Delegate.CreateDelegate etc), but you can then wrap it to an EventHandler, or the reverse.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!