Reputation: 2583
I have a very simple class:
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);
}
}
in the Main I create two instances and connect their events:
var plugin1 = new Plugin("plugin1") { member1 = "AAA1", member2="BBB1"};
var plugin2 = new Plugin("plugin2") { member1 = "AAA1", member2 = "BBB1" };
plugin1.OnStatusChange1_OUT += plugin2.PluginAction;
now I'm perfectly aware that this makes little sense but I want to go deep in reflection and get all information I can get:
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);
}
Now I would like to get all information about events:
EVENTS:
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);
}
The part that I have not been able to do is to understand what events are connected to what. In short I would like to add a part that tells me what events are connected to what and what is instead not connected. Something like:
Plugin1 OnStatusChange1_Out connectedTo plugin2.PluginAction OnStatusChange1_Out connectedTo NULL
and this obviously has to be do through reflection and in the same foreach loop above.
Thank you in advance for any help Patrick
ADD for Jeroen van Langen:
What you wrote works. I have tried to put the proposed solution in the reflection loop so:
public void CheckEventInfo(Plugin plg, ChangedEventHandler_OUT ev)
{
string invocationList = null;
if (ev != null)
foreach (var item in ev.GetInvocationList())
invocationList += item.Method.Name;
}
but then
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);
}
So here I don't know what to put since ev is an eventinfo and not ChangedEventHandler_OUT. Could you please help me? Thanks
Upvotes: 2
Views: 3277
Reputation: 15197
I suppose you would like to do this only for educational purposes. Generally speaking, if you want to know whether a particular event has any subscribers or, worse, the exactly list of the subscribers, then you've made an architectural mistake. (The single exception exists for the event invocation method which should check the event delegate for nullity before calling.)
Here is a way how to get the desired result.
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;
}
You have to keep in mind, that Target
property can be be null
for static events. To obtain static events too, include BindingFlags.Static
into the GetField() method second parameter.
Upvotes: 1
Reputation: 22038
Using the plugin1.GetType().GetEvents()
will only get the definition of events. Like dotctor said, you need to get the InvocationList of the event. From there you could get some instance information.
Here's an example:
// 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;
}
I think this should be enough to create your own solution.
Upvotes: 1