如何使用反射将事件处理程序附加到事件?

时间:2010-06-25 18:22:09

标签: c# events reflection event-handling delegates

我知道可用于将处理程序附加到事件的EventInfo.AddEventHandler(...)方法。但是,如果我甚至无法定义事件处理程序的正确签名,应该怎么做,因为我甚至没有引用处理程序所期望的事件args?

我将用正确的代码解释问题。

//当我在我的解决方案中提供一切可用的场景时,Zero Reflection Scenario。

internal class SendCommentsManager
{
    public void Customize(IRFQWindowManager rfqWindowManager)
    {
        rfqWindowManager.SendComment += HandleRfqSendComment;
    }

    private void HandleRfqSendComment(object sender, SendCommentEventArgs args)
    {
        args.Cancel = true;
    }
}

现在,我希望通过使用反射来实现相同的目标。我已经能够找出大部分内容但是当我将一个委托附加到事件时(使用AddEventHandler)它会引发"Error binding to target method."异常。

我理解这个异常背后的原因,将错误的委托附加到事件中。但必须有一些方法来实现这一目标。

 internal class SendCommentsManagerUsingReflection
 {
     public void Customize(IRFQWindowManager rfqWindowManager)
     {
         EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
         eventInfo.AddEventHandler(rfqWindowManager, 
             Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "HandleRfqSendComment"));
         //<<<<<<<<<<ABOVE LINE IS WHERE I AM GOING WRONG>>>>>>>>>>>>>>
     }

     private void HandleRfqSendComment(object sender, object args)
     {
         Type sendCommentArgsType = args.GetType();
         PropertyInfo cancelProperty = sendCommentArgsType.GetProperty("Cancel");
         cancelProperty.SetValue(args, true, null);
     }
 }

2 个答案:

答案 0 :(得分:10)

我认为您的代码失败了,因为HandleRfqSendComment是私有的。相反,您可以直接创建该方法的委托,而不将其名称传递给CreateDelegate。然后,您需要使用以下方法将委托转换为所需类型:

public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
    return Delegate.CreateDelegate(
        targetDelegateType,
        originalDelegate.Target,
        originalDelegate.Method);
}

在您的代码中,您可以按如下方式使用此方法:

EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
Action<object, object> handler = HandleRfqSendComment;
Delegate convertedHandler = ConvertDelegate(handler, eventInfo.EventHandlerType);
eventInfo.AddEventHandler(rfqWindowManager, convertedHandler);

答案 1 :(得分:0)

这里已经很棒的答案的一个小补充。这是一个可用于订阅带有操作的事件的辅助类。

public static partial class ReactiveExtensions
{
    public static EventHandler<TEvent> CreateGenericHandler<TEvent>(object target, MethodInfo method)
    {
        return (EventHandler<TEvent>)Delegate
            .CreateDelegate(typeof(EventHandler<TEvent>), 
            target, method);
    }

    public static EventHandler CreateHandler(object target, MethodInfo method)
    {
        return (EventHandler)Delegate
            .CreateDelegate(typeof(EventHandler),
            target, method);
    }
        
        
    static void BindEventToAction(object target, EventInfo eventInfo, Delegate action)
    {
        MethodInfo method;

        if (eventInfo.EventHandlerType.IsGenericType)
        {
            method = typeof(ReactiveExtensions)
                .GetMethod(nameof(CreateGenericHandler))
                .MakeGenericMethod(
                eventInfo.EventHandlerType.GetGenericArguments());
        }
        else
        {
            method = typeof(ReactiveExtensions)
                .GetMethod(nameof(CreateHandler));
        }


        Delegate @delegate = (Delegate)method.Invoke(null,
            new object[] { action.Target, action.Method });


        eventInfo.AddEventHandler(target, @delegate);
    }
}

以下是有关如何使用它的示例:

public static partial class ReactiveExtensions
{
    public static void Subscribe<T>(T source, string eventName)
    {
        EventInfo eventInfo = typeof(T).GetEvent(eventName);

        Action<object, object> action = (s, e) =>
        {
            Console.WriteLine("Event Called");
        };

        BindEventToAction(source, eventInfo, action);
    }
}