如何从EventInfo获取委托对象?
我需要从当前类中获取所有事件,并找出订阅它的方法。 在这里,我得到了一些关于如何做到这一点的答案 ,但我不知道如何获得delegate
,而我所拥有的只是EventInfo
。
var events = GetType().GetEvents(); foreach (var e in events) { Delegate d = e./*GetDelegateFromThisEventInfo()*/; var methods = d.GetInvocationList(); }
是否可以通过EventInfo
获得委托? 怎么样?
语句var events = GetType().GetEvents();
获取与当前类型关联的EventInfo
对象列表,而不是当前实例本身。 因此, EventInfo
对象不包含有关当前实例的信息,因此它不知道有线EventInfo
。
要获取所需信息,您需要获取当前实例上事件处理程序的支持字段。 这是如何做:
public class MyClass { public event EventHandler MyEvent; public IEnumerable GetSubscribedMethods() { Func 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 b__0
如果您希望返回代理而不是方法信息等,我相信您可以使用代码进行浏览。
我希望这有帮助。
对于我的情况,字段值(类ToolStripMenuItem
,字段EventClick
)令人沮丧地是对象类型,而不是委托。 我不得不求助于Les在他的回答中提到的Events
,就像我从这里得到的那样。 在这种情况下,字段EventClick
仅保存存储在此属性中的EventHandlerList的键。
其他一些评论:
- 我使用的规则
fieldName = "Event" + eventName
在每种情况下都不起作用。 遗憾的是,没有将事件名称链接到字段名称的通用规则。 您可能会尝试使用静态字典进行映射,类似于本文 。 -
我不太确定BindingFlags。 在另一种情况下,您可能需要调整它们。
///
/// Gets the EventHandler delegate attached to the specified event and object /// /// object that contains the event /// name of the event, eg "Click" 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; }
与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; }
…只要事件处理程序在类中声明,如上所示。 但是考虑将EventHandlerList存储在“Events”属性中的Control类,每个事件字段名称以“Event”开头,后跟事件名称。 然后是Form派生类似乎以不同方式管理事件。 值得深思。