【问题标题】:How to get a delegate object from an EventInfo?如何从 EventInfo 中获取委托对象?
【发布时间】:2010-09-23 23:43:03
【问题描述】:

我需要从当前类中获取所有事件,并找出订阅它的方法。 Here I got some answers on how to do that,但是当我只有EventInfo 时,我不知道如何获得delegate

var events = GetType().GetEvents();

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

是否可以使用EventInfo 获得代表?怎么样?

【问题讨论】:

  • 引用您上一个问题的最高投票答案:“现在我想您可以尝试找到“添加”处理程序的主体,反编译并计算出事件处理程序的存储方式,然后以这种方式获取它们...但请不要。您正在创建大量工作,只是为了打破封装。只需重新设计您的代码,以便您不需要这样做。”我完全同意。

标签: c# .net reflection


【解决方案1】:

语句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

如果您希望返回委托而不是方法信息等,我相信您可以使用代码。

我希望这会有所帮助。

【讨论】:

【解决方案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 派生类,它们似乎以不同的方式管理事件。值得深思。

【讨论】:

  • 这非常出色。这段代码很难从第一原则推导出来,谢谢@Les。
【解决方案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;
    }
    

【讨论】:

  • 帮了我很多忙。谢谢!
  • 您可以通过 eventHandlers[value].GetInvocationList() 获取所有委托 - 上面的代码最多返回一个委托。
猜你喜欢
  • 1970-01-01
  • 2021-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-11
  • 2011-01-19
  • 2013-12-20
  • 2019-08-31
相关资源
最近更新 更多