Call a method when a generic event occurs

2019-09-14 08:31发布

问题:

I'm facing a problem when trying to implement a call to a method fired up by an event which should be defined during run-time. I found this answer:

Redirecting to a dynamic method from a generic event handler

and implemented that solution, but I keep getting an exception when the method to call is an instance one, not static. Here is my partial code:

public class Operation
{
    public bool EventFired 
    {
        get { return _eventFired; }
    }

    private bool _eventFired = false;

    public void Execute(object source, string EventName)
    {
        EventInfo eventInfo = source.GetType().GetEvent(EventName);
        Delegate handler = null;

        Type delegateHandler = eventInfo.EventHandlerType;
        MethodInfo invokeMethod = delegateHandler.GetMethod("Invoke");
        ParameterInfo[] parms = invokeMethod.GetParameters();

        Type[] parmTypes = new Type[parms.Length];

        for (int i = 0; i < parms.Length; i++)
            parmTypes[i] = parms[i].ParameterType;

        DynamicMethod customMethod = new DynamicMethod
            (
            "TempMethod",
            invokeMethod.ReturnType,
            parmTypes,
            typeof (Operation).Module
            );

        MethodInfo inf = typeof (Operation).GetMethod("SetFlag", BindingFlags.Instance | BindingFlags.Public);

        ILGenerator ilgen = customMethod.GetILGenerator();
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Call, inf);
        ilgen.Emit(OpCodes.Ret);

        //handler = _customMethod.CreateDelegate(delegateHandler);          // This works if I change SetFlag() to static 

        handler = customMethod.CreateDelegate(delegateHandler, this);       // I get an ArgumentException at this point: 
                                                                            //Cannot bind to the target method because its signature 
                                                                            //or security transparency is not compatible with that of the delegate type.   



        eventInfo.AddEventHandler(source, handler);
    }

    /// <summary>Signals that the event has been raised.</summary>
    public void SetFlag()
    {
        _eventFired = true;
    }

}

I can't find a way to get rid of that exception, the code works fine if I turn SetFlag() method to static, but that's not what I need. Any suggestions would be very appreciated. Thanks in advance.

回答1:

I finally managed to resolve the problem by slightly modifying the DynamicMethod definition, as described in this answer:

Reference 'this' in dynamic event handler

If I add my class type as first value in the Type array, which is then passed as parameter to the DynamicMethod, the delegate is correctly created and it works with instance methods!

public class Operation
{
    public bool EventFired 
    {
        get { return _eventFired; }
    }

    private bool _eventFired = false;

    public void Execute(object source, string EventName)
    {
        EventInfo eventInfo = source.GetType().GetEvent(EventName);
        Delegate handler = null;

        Type delegateHandler = eventInfo.EventHandlerType;
        MethodInfo invokeMethod = delegateHandler.GetMethod("Invoke");
        ParameterInfo[] parms = invokeMethod.GetParameters();

        Type[] parmTypes = new Type[parms.Length + 1];

        parmTypes[0] = this.GetType();  //First parameter is this class type.

        for (int i = 0; i < parms.Length; i++)
            parmTypes[i + 1] = parms[i].ParameterType;

        DynamicMethod customMethod = new DynamicMethod
                                    (
                                        "TempMethod",
                                        invokeMethod.ReturnType,
                                        parmTypes
                                    );

        MethodInfo inf = typeof (Operation).GetMethod("SetFlag", BindingFlags.Instance | BindingFlags.Public);

        ILGenerator ilgen = customMethod.GetILGenerator();
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Call, inf);
        ilgen.Emit(OpCodes.Ret);

        handler = customMethod.CreateDelegate(delegateHandler, this);      

        eventInfo.AddEventHandler(source, handler);
    }

    /// <summary>Signals that the event has been raised.</summary>
    public void SetFlag()
    {
        _eventFired = true;
    }

}