2010-09-23 50 views
7

我需要從當前類中獲取所有事件,並找出訂閱它的方法。 Here I got some answers on how to do that,但我不知道如何獲得delegate當我擁有的是EventInfo如何從EventInfo獲取委託對象?

var events = GetType().GetEvents(); 

foreach (var e in events) 
{ 
    Delegate d = e./*GetDelegateFromThisEventInfo()*/; 
    var methods = d.GetInvocationList(); 
} 

是否有可能得到EventInfo代表?怎麼樣?

+1

從你前面的問題的最高投票的回答引用:「現在我想你可以嘗試找到的身體‘加’處理,反編譯,並制定出如何的事件處理程序正在存儲並以這種方式獲取它們...... **但是請不要**。您正在創建大量工作,只是爲了打破封裝,只是**重新設計代碼**,以便您不需要做這個。」我完全同意。 – 2010-09-24 00:39:32

回答

12

語句var events = GetType().GetEvents();可以獲取與當前類型關聯的對象列表EventInfo,而不是當前實例本身。所以EventInfo對象不包含有關當前實例的信息,因此它不知道有線代理。

要獲取您需要的信息,您需要獲取當前實例上事件處理程序的支持字段。具體方法如下:

public class MyClass 
{ 
    public event EventHandler MyEvent; 

    public IEnumerable<MethodInfo> GetSubscribedMethods() 
    { 
     Func<EventInfo, FieldInfo> ei2fi = 
      ei => this.GetType().GetField(ei.Name, 
       BindingFlags.NonPublic | 
       BindingFlags.Instance | 
       BindingFlags.GetField); 

     return from eventInfo in this.GetType().GetEvents() 
       let eventFieldInfo = ei2fi(eventInfo) 
       let eventFieldValue = 
        (System.Delegate)eventFieldInfo.GetValue(this) 
       from subscribedDelegate in eventFieldValue.GetInvocationList() 
       select subscribedDelegate.Method; 
    } 
} 

所以,現在你的調用代碼可以是這樣的:

class GetSubscribedMethodsExample 
{ 
    public static void Execute() 
    { 
     var instance = new MyClass(); 
     instance.MyEvent += new EventHandler(MyHandler); 
     instance.MyEvent += (s, e) => { }; 

     instance.GetSubscribedMethods() 
      .Run(h => Console.WriteLine(h.Name)); 
    } 

    static void MyHandler(object sender, EventArgs e) 
    { 
     throw new NotImplementedException(); 
    } 
} 

從上面的輸出是:

MyHandler 
<Execute>b__0 

我敢肯定,你可以夾具周圍代碼如果你想返回代表而不是方法信息等。

我希望這可以幫助。

+0

這真的很神祕,我甚至都讀不懂。 – jcmcbeth 2011-04-18 19:26:44

+0

@jcmcbeth - 什麼部分是神祕的? – Enigmativity 2011-05-19 08:32:59

+1

您的EventInfo FieldInfo委託(ei2fi)似乎不適用於[自定義添加和刪除事件](http://msdn.microsoft.com/zh-cn/magazine/cc163533.aspx)。 – 2012-03-23 18:48:06

2

類似於Enigmativity,調用列表可以用於其他類,而不僅僅是當前的類中找到...

private void testit() 
    { 
     WithEvents we = new WithEvents(); 
     we.myEvent += new EventHandler(we_myEvent); 
     we.myEvent += new EventHandler(we_myEvent2); 

     foreach (EventInfo ev in we.GetType().GetEvents()) 
     { 
      FieldInfo fi = we.GetType().GetField(ev.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); 
      Delegate del = (Delegate)fi.GetValue(we); 
      var list = del.GetInvocationList(); 
      foreach (var d in list) 
      { 
       Console.WriteLine("{0}", d.Method.Name); 
      } 
     } 
    } 

    void we_myEvent(object sender, EventArgs e) 
    { 
    } 
    void we_myEvent2(object sender, EventArgs e) 
    { 
    } 


public class WithEvents 
{ 
    public event EventHandler myEvent; 
} 

...只要事件處理程序在班上擔任聲明我們在上面看到。但請考慮Control類,其中EventHandlerList存儲在「Events」屬性中,並且每個事件字段名稱均以「Event」開頭,後跟事件名稱。然後是Form派生類似乎管理事件的方式不同。食物的思想。

2

對我而言,字段值(類ToolStripMenuItem,字段EventClick)令人沮喪地是對象類型,而不是委託。我不得不求助於Les在他的回答中提到的Events屬性,用我從here得到的方式。在這種情況下字段EventClick僅保存存儲在此屬性中的EventHandlerList的鍵。

其他一些言論:

  • 我使用不會在任何情況下工作規則fieldName = "Event" + eventName。不幸的是,沒有將事件名稱與字段名稱鏈接的通用規則。您可以嘗試使用靜態字典進行映射,類似於this article
  • 我從來不確定BindingFlags。在另一種情況下,您可能需要調整它們。

    /// <summary> 
    /// Gets the EventHandler delegate attached to the specified event and object 
    /// </summary> 
    /// <param name="obj">object that contains the event</param> 
    /// <param name="eventName">name of the event, e.g. "Click"</param> 
    public static Delegate GetEventHandler(object obj, string eventName) 
    { 
        Delegate retDelegate = null; 
        FieldInfo fi = obj.GetType().GetField("Event" + eventName, 
                  BindingFlags.NonPublic | 
                  BindingFlags.Static | 
                  BindingFlags.Instance | 
                  BindingFlags.FlattenHierarchy | 
                  BindingFlags.IgnoreCase); 
        if (fi != null) 
        { 
         object value = fi.GetValue(obj); 
         if (value is Delegate) 
          retDelegate = (Delegate)value; 
         else if (value != null) // value may be just object 
         { 
          PropertyInfo pi = obj.GetType().GetProperty("Events", 
                  BindingFlags.NonPublic | 
                  BindingFlags.Instance); 
          if (pi != null) 
          { 
           EventHandlerList eventHandlers = pi.GetValue(obj) as EventHandlerList; 
           if (eventHandlers != null) 
           { 
            retDelegate = eventHandlers[value]; 
           } 
          } 
         } 
        } 
        return retDelegate; 
    } 
    
+0

幫助我解決了很多問題。謝謝! – Law 2017-02-27 19:56:55