Firing Events using reflection

M

mswlogo

I looked high and low for code to do this and finally found some VB
code that did it right.

This is a C# flavor of it.

public event EventHandler<EventArgs> MyEventToBeFired;

public void FireEvent(Guid instanceId, string handler)
{
EventArgs e = new EventArgs(instanceId);

MulticastDelegate eventDelagate =
(MulticastDelegate)GetType().GetField(handler,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).GetValue(this);

Delegate[] delegates = eventDelagate.GetInvocationList();

foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, new object[] { this, e
});
}
}

FireEvent("Some Data string arg that so happens to be a GUID",
"MyEventToBeFired");
 
M

Marc Gravell

Firing events via reflection is always going to be dodgy as heck. In
particular, you have know way of knowing what the backer is. Is *might* be a
compiler-built field, but it might be a separate field I provide (perhaps
through a facade to an encapsulated class), or it might be via an
EventHandlerList, or about 17 other things. So you can't rely on reflection
to work unless you know what class you are dealing with and how the event is
implemented.

Even within a class you own, this would mandate some kind of switch or
dictionary; as a thought, it would be quite easy to create a variant of
EventHandlerList that works on Dictionary<string, EventHandler>, and lets
you do all of this just using the handler (string) directly as the key...
i.e. something like below...

But as a general principal, events should be fired from *within* a class,
not from outside.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;

public class MyData
{
public event EventHandler SomeEvent1 {
add { events.Add("SomeEvent1", value); }
remove { events.Remove("SomeEvent1", value); }
}
private readonly DelegateDictionary events = new DelegateDictionary();
public event EventHandler SomeEvent2
{
add { events.Add("SomeEvent2", value); }
remove { events.Remove("SomeEvent2", value); }
}
public event EventHandler SomeEvent3
{
add { events.Add("SomeEvent3", value); }
remove{ events.Remove("SomeEvent3", value); }
}
public void FireEvent(string name)
{
events.InvokeHandler(name, this);
}

}
class Program
{
static void Main()
{
MyData data = new MyData();
data.SomeEvent1 += new EventHandler(data_SomeEvent1);
data.SomeEvent2 += new EventHandler(data_SomeEvent2);
data.SomeEvent2 += new EventHandler(data_SomeEvent2);
data.FireEvent("SomeEvent2");
data.FireEvent("SomeEvent1");
data.FireEvent("SomeEvent3");// not subscribed
data.FireEvent("SomeDuffEvent");
}

static void data_SomeEvent1(object sender, EventArgs e)
{
Debug.WriteLine("data_SomeEvent1");
}
static void data_SomeEvent2(object sender, EventArgs e)
{
Debug.WriteLine("data_SomeEvent2");
}
}

public class DelegateDictionary
{
private readonly Dictionary<string, Delegate> data = new
Dictionary<string, Delegate>();

public void InvokeHandler(string key, object sender)
{
EventHandler handler = GetHandler(key);
if (handler != null) handler(sender, EventArgs.Empty);
}
public void Invoke(string key, params object[] parameters)
{
Delegate handler = this[key];
if (handler != null) handler.DynamicInvoke(parameters);
}
public EventHandler GetHandler(string key)
{
return (EventHandler) this[key];
}
public T Get<T>(string key)
{
return (T)(object)this[key];
}
public Delegate Get(string key)
{
return this[key];
}
public void Add(string key, Delegate handler)
{
if (handler == null) return;
Delegate current;
if (data.TryGetValue(key, out current))
{
data[key] = Delegate.Combine(current,handler);
}
else
{
data.Add(key, handler);
}
}
public void Remove(string key, Delegate handler)
{
if (handler == null) return;
Delegate current;
if (data.TryGetValue(key, out current))
{
data[key] = Delegate.Remove(current, handler);
}
}
public Delegate this[string key]
{
get
{
Delegate handler;
data.TryGetValue(key, out handler);
return handler;
}
}
}
 
M

Marc Gravell

Minor edit to clean up the dictionary when un-subscribing; you could even go
overboard and create / release the "data" field as the count of delegates
changes between 0 and >0

Still not sure it is a great idea *for calling from externally*, but an
interesting diversion ;-p

Marc

public void Remove(string key, Delegate handler) {
if (handler == null) return;
Delegate current;
if (data.TryGetValue(key, out current))
{
Delegate result = Delegate.Remove(current, handler);
if (result == null) {
data.Remove(key);
} else {
data[key] = result;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top