我需要从当前类中获取所有事件,并找出订阅它的方法。 Here I got some answers on how to do that,但我不知道如果我拥有delegate
,我将如何获得EventInfo
。
var events = GetType().GetEvents();
foreach (var e in events)
{
Delegate d = e./*GetDelegateFromThisEventInfo()*/;
var methods = d.GetInvocationList();
}
是否可以获得EventInfo
的代表?怎么样?
答案 0 :(得分: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
如果您希望返回代理而不是方法信息等,我确信您可以使用代码进行浏览。
我希望这会有所帮助。
答案 1 :(得分:3)
与 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 :(得分:3)
对于我的情况,字段值(类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;
}