2015-05-18 59 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分配。 –