如何通过反射获取事件目标方法

时间:2016-06-15 09:31:59

标签: c# events reflection

我有一个非常简单的课程:

public delegate void ChangedEventHandler_OUT();
public class Plugin
{

    public Plugin(string _pluginName)
    {
        pluginName = _pluginName;
    }

    public string pluginName;
    public string member1;
    public string member2;

    public event ChangedEventHandler_OUT OnStatusChange1_OUT;
    public event ChangedEventHandler_OUT OnStatusChange2_OUT;

    public void PluginAction()
    {
        MessageBox.Show("ACtion from plugin " + pluginName);
    }

}
在主要我创建两个实例并连接他们的事件:

var plugin1 = new Plugin("plugin1") { member1 = "AAA1", member2="BBB1"};
var plugin2 = new Plugin("plugin2") { member1 = "AAA1", member2 = "BBB1" };

plugin1.OnStatusChange1_OUT += plugin2.PluginAction;

现在我完全意识到这没什么意义,但我想深入思考并获得我能得到的所有信息:

  

MEMBERS

lbxInfo.Items.Add("Plugin1 member:");
Type type = typeof(Plugin);
foreach (var field in type.GetFields())
{
        string strName = field.Name; // Get string name
        string strType = field.FieldType.Name;
        string strValue = (string)field.GetValue(plugin1);
        lbxInfo.Items.Add(strType + " " + strName + " = " + strValue);
}

这很好用 enter image description here

现在我想获得有关事件的所有信息:

  

事件:

lbxInfo.Items.Add("Plugin1 events:");
foreach (var ev in plugin1.GetType().GetEvents())
{
    string strName = ev.Name;
    string strType = ev.EventHandlerType.Name;
    MethodInfo strConnectedTo = ev.GetAddMethod();

    lbxInfo.Items.Add("Name = " + strName + " type =" + strType);

}

enter image description here

我无法做的部分是了解哪些事件与哪些事件有关。简而言之,我想添加一个部分,告诉我哪些事件与什么相关,哪些事情没有连接。类似的东西:

  

Plugin1   OnStatusChange1_Out connectedTo plugin2.PluginAction   OnStatusChange1_Out connectedTo NULL

这显然必须通过反思和上面相同的foreach循环来完成。

提前感谢您的帮助 帕特里克

为Jeroen van Langen添加:

你写的作品。 我试图将提出的解决方案放在反射循环中:

public void CheckEventInfo(Plugin plg, ChangedEventHandler_OUT ev)
{
    string invocationList = null;
    if (ev != null)
            foreach (var item in ev.GetInvocationList())
                invocationList += item.Method.Name;
}

但是

        lbxInfo.Items.Add("Plugin1 events:");
        foreach (var ev in plugin1.GetType().GetEvents())
        {
            string strName = ev.Name;
            string strType = ev.EventHandlerType.Name;
            MethodInfo strConnectedTo = ev.GetAddMethod();

            CheckEventInfo( plugin1,ev);<------NO!
            lbxInfo.Items.Add("Name = " + strName + " type =" + strType);
        }

所以这里我不知道要放什么,因为ev是一个eventinfo而不是ChangedEventHandler_OUT。 请你帮助我好吗?感谢

2 个答案:

答案 0 :(得分:1)

使用plugin1.GetType().GetEvents()只能获得事件的定义。像dotctor一样,你需要得到事件的InvocationList。从那里你可以获得一些实例信息。

以下是一个例子:

// This class will create an instance of the MainClass.
class Program
{
    static void Main(string[] args)
    {
        MainClass mainClass = new MainClass();

        mainClass.WhateverName = "MyClass2 main";
        mainClass.Run();

        Console.ReadLine();
    }
}
// I'd rather use an interface
public interface IMyClassInfo
{
    string WhateverName { get; set; }
}
// This mainclass will create a EventHandleClass which contains an event.
// This event is binded to the Class1_Example method.
public class MainClass : IMyClassInfo
{
    internal void Run()
    {
        EventHandleClass eventHandleClass = new EventHandleClass();
        eventHandleClass.Example += Class1_Example;
        eventHandleClass.CheckEventInfo();
    }

    private void Class1_Example(object sender, EventArgs e)
    {
        Console.WriteLine("Class1_Example Method: ");
    }

    public string WhateverName { get; set; }

}
// this class holds the event, and when executing `CheckEventInfo()` the 
// invocationlist is iterated and checkt if the target object is of type IMyClassInfo
public class EventHandleClass
{

    public void CheckEventInfo()
    {

        if (Example != null)
            foreach (var item in Example.GetInvocationList())
            {
                Console.WriteLine("GetInvocationList method:" + item.Method.Name);
                Console.WriteLine("GetInvocationList class:" + item.Method.DeclaringType.FullName);

                if (item.Target is IMyClassInfo)
                {
                    Console.WriteLine("class2.WhateverName: " + ((IMyClassInfo)item.Target).WhateverName);
                }
                item.DynamicInvoke(this, EventArgs.Empty);
            }

    }

    public event EventHandler Example;
}

我认为这应该足以创建自己的解决方案。

答案 1 :(得分:1)

我想你只想出于教育目的这样做。一般来说,如果你想知道特定事件是否有任何订阅者,或者更糟糕的是,确切地说是订阅者的列表,那么你就犯了一个架构错误。 (事件调用方法存在唯一的例外,它应该在调用之前检查事件委托是否为null。)

以下是如何获得所需结果的方法。

string GetSubscriptions(object o)
{
    Type t = o.GetType();

    // Obtain the collection of EventInfo's
    var events = t.GetEvents();
    if (events.Length == 0)
    {
        return "No events in " + t.Name;
    }

    string result = string.Empty;
    foreach (var item in events)
    {
        result += "Event " + item.Name + ": ";

        // Get the event's backing field description (FieldInfo)
        var ed = t.GetField(item.Name, BindingFlags.Instance | BindingFlags.NonPublic);
        if (ed == null)
        {
            throw new InvalidOperationException("Event backing field could not be obtained");
        }

        // Get the value of the backing field
        var dl = ed.GetValue(o) as Delegate;

        // If value is not null, then we've subscriptions
        if (dl != null)
        {
            // Get the invocation list - an array of Delegate
            var il = dl.GetInvocationList();

            // This check is actually not needed, since the array should always contain at least 1 item
            if (il.Length != 0)
            {
                // Use Target property of the delegate to get a reference to the object the delegate's method belongs to
                result += string.Join("; ", il.Select(i => i.Target.GetType().Name + "." + i.Method.Name)) + Environment.NewLine;
                continue;
            }
        }

        result += "no subscriptions" + Environment.NewLine;
    }

    return result;
}

您必须记住,对于静态事件,Target属性可以是null。要获取静态事件,请将BindingFlags.Static包含在GetField()方法的第二个参数中。