如何从EventInfo获取委托对象?

时间:2010-09-23 23:43:04

标签: c# .net reflection

我需要从当前类中获取所有事件,并找出订阅它的方法。 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的代表?怎么样?

3 个答案:

答案 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;
    }