2015-05-18 56 views
8

我试图将一个委托附加到不同委托的调用列表。由此,我正在实现对现有事件的一种挂钩。 我需要挂接每个被调用的事件后运行的东西。反射 - 将委托添加到另一个委托的调用列表

以下示例工程只要Delegate暴露的类型和我传入的Action具有完全相同的签名。 (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() { } 
} 

的问题:当与测试的事件修饰符暴露的委托不具有相同的签名,我收到了很好想要的,明显的例外,其状态(在我的话)Action不能被添加到Action<int>的调用列表。说得通。

只是要清楚我所描述如下:

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

什么我要找的是创建相同类型EventHandlerType的另一位代表的方式。为了做到这一点,我需要创建一个EventHandlerType的签名i的方法,它会在内部调用操作。

类似:

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); 
     } 
} 
+0

你想通过'Action '作为'Action'并添加到'AddHandler'中? –

+0

每次调用任何@事件的底层代理时,我都想调用Action(为了参数的作用,类型为“是”)。 我已经描述了我的尝试,但任何解决方案都将是最受欢迎的。 –

回答

10

这似乎是工作......有内部的各种意见......我不知道这是否是做的最好的方式。我正在构建一个Expression树来执行委托调用。

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()); 
     } 
    } 
} 
+0

我正在研究类似的答案,但你击败了我:)很好的实施。 –

+0

看起来不错,让我试试看。 –

+0

惊人的:) 10x分配。 –