反射 - 将代理添加到另一个代理的调用列表

时间:2015-05-18 11:16:01

标签: c# reflection delegates

我试图将一个委托附加到另一个委托的调用列表中。 通过这个我在现有事件上实现了一种钩子。 我需要挂钩在每个被调用的事件之后运行的东西。

以下示例可以,只要类型公开的委托和传入的Action i具有完全相同的签名即可。 (On1和OnAll事件都使用Action委托声明,因此它可以工作)。

代码:我如何将Action与事件修饰符公开的现有委托挂钩。

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中使用事件修饰符公开的委托没有相同的签名时,我得到了一个非常需要且明显的异常,其中说明了(在我看来)那个{{ 1}}无法添加到Action 的调用列表中。说得通。

为了清楚起见我正在描述以下内容:

Action<int>

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

类似的东西:

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

1 个答案:

答案 0 :(得分:10)

这似乎有效......里面有各种各样的评论......我不确定这是否是最好的方法。我正在构建一个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()); } } } 树来执行委托调用。

{{1}}