反射 - 将代理添加到另一个代理的调用列表 [英] Reflection - Add a Delegate to another Delegate's invocation list

查看:144
本文介绍了反射 - 将代理添加到另一个代理的调用列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将代理附加到不同代理的调用列表。
由于我在现有的事件上实现了一种Hook。
我需要连接每个被调用的事件后运行的东西。

i am attempting to attach a Delegate to an invocation list of a different delegate. By that i am achieving a kind of Hook on existing events. I need to hook up something that runs after each event that is invoked.

只要代理程序,以下示例的工作原理暴露的类型和我通过的Action具有完全相同的签名。
(On1和OnAll事件都被声明为一个Action代理,所以它的工作原理)。

The following example works as long as the Delegate exposed by the type and the Action i pass in have the exact same signature. (On1 and OnAll events are both declared with an Action delegate so it works).

代码:我如何连接一个现有代理的操作一个事件修饰符。

Code : How i hook up an Action with an existing delegate exposed by an event modifier.

public static class ReflectionExtensions
{
    public static IEnumerable<EventInfo> GetEvents(this object obj)
    {
        var events = obj.GetType().GetEvents();
        return events;
    }

    public static void AddHandler(this object obj, Action action)
    {
        var events = obj.GetEvents();
        foreach (var @event in events)
        {                    
             @event.AddEventHandler(obj, action);
        }
    }
}

样本:

public class Tester 
{
    public event Action On1;
    public event Action On2;

    public void RaiseOn1()
    {
        On1();
    }

    public void RaiseOn2()
    {
        On2();
    }
}   

class Program
{
    static void Main(string[] args)
    {
        var t = new Tester();
        t.On1 += On1;
        t.On2 += On2;

        t.AddHandler(OnAll);

        t.RaiseOn1();
        t.RaiseOn2();
    }

    public void On1() { }
    public void On2() { }
    public void OnAll() { }
} 

问题:当在Tester中使用事件修饰符公开的代理没有相同的签名我得到一个很好的想要和明显的异常,它表示(在我的话中) Action 不能添加到<$ c $的调用列表c> Action< int> 。说得通。

The Problem : When the Delegate exposed with an event modifier in Tester does not have the same signature i get a well wanted and obvious exception which states (in my words) that Action can't be added to an invocation list of an Action<int> . makes sense.

只是为了清楚我正在描述以下内容:

Just to be clear I'm describing the following :

    public event Action<int> On1;    
    public void On1(int i){}

我正在寻找的是方式来创建与EventHandlerType相同类型的另一个代理。为了做到这一点,我需要创建一个具有EventHandlerType的签名i的方法,该方法将在内部调用操作。

What I'm looking for is a way to create another Delegate of the same type as the EventHandlerType. In order to do that i need to create a method with the signature i of EventHandlerType which would internally invoke action.

如下:

 public static void AddHandler(this object obj, Action action)
 {
      var events = obj.GetEvents();
      foreach (var @event in events)
      {
          // method with the signeture of EventHandlerType which does action();
          MethodInfo wrapperMethod = WrapAction(@event.EventHandlerType, action);

          Delegate handler = Delegate.CreateDelegate(@event.EventHandlerType, action.Target, wrapperMethod);
          @event.AddEventHandler(obj, handler);
      }
 }


推荐答案

似乎工作...里面有各种各样的意见...我不知道这是否是最好的方法。我正在构建一个表达式树来执行委托调用。

This seems to work... There are various comments inside... I'm not sure if this is the best way to do it. I'm building an Expression tree to do the delegate invocation.

public static void AddHandler(this object obj, Action action)
{
    var events = obj.GetEvents();

    foreach (var @event in events)
    {
        // Simple case
        if (@event.EventHandlerType == typeof(Action))
        {
            @event.AddEventHandler(obj, action);
        }
        else
        {
            // From here: http://stackoverflow.com/a/429564/613130
            // We retrieve the parameter types of the event handler
            var parameters = @event.EventHandlerType.GetMethod("Invoke").GetParameters();

            // We convert it to ParameterExpression[]
            ParameterExpression[] parameters2 = Array.ConvertAll(parameters, x => Expression.Parameter(x.ParameterType));

            MethodCallExpression call;

            // Note that we are "opening" the delegate and using
            // directly the Target and the Method! Inside the 
            // LambdaExpression we will build there won't be a 
            // delegate call, there will be a method call!
            if (action.Target == null)
            {
                // static case
                call = Expression.Call(action.Method);
            }
            else
            {
                // instance type
                call = Expression.Call(Expression.Constant(action.Target), action.Method);
            }

            // If you are OK to create a delegate that calls another
            // delegate, you can:
            // call = Expression.Call(Expression.Constant(action), typeof(Action).GetMethod("Invoke"));
            // instead of the big if/else

            var lambda = Expression.Lambda(@event.EventHandlerType, call, parameters2);
            @event.AddEventHandler(obj, lambda.Compile());
        }
    }
}

这篇关于反射 - 将代理添加到另一个代理的调用列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆