How do I raise an event via reflection in .NET/C#?
Here's a demo using generics (error checks omitted):
using System;
using System.Reflection;
static class Program {
private class Sub {
public event EventHandler<EventArgs> SomethingHappening;
}
internal static void Raise<TEventArgs>(this object source, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs
{
var eventDelegate = (MulticastDelegate)source.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source);
if (eventDelegate != null)
{
foreach (var handler in eventDelegate.GetInvocationList())
{
handler.Method.Invoke(handler.Target, new object[] { source, eventArgs });
}
}
}
public static void Main()
{
var p = new Sub();
p.Raise("SomethingHappening", EventArgs.Empty);
p.SomethingHappening += (o, e) => Console.WriteLine("Foo!");
p.Raise("SomethingHappening", EventArgs.Empty);
p.SomethingHappening += (o, e) => Console.WriteLine("Bar!");
p.Raise("SomethingHappening", EventArgs.Empty);
Console.ReadLine();
}
}
Raise an event via reflection in c#
Events in WinForms are generally overridden and not don't have a one-to-one delegate backing. Instead the class (basically) has a dictionary of the event->delegate mappings and the delegates are only created when the events are added. So you can't assume there's a delegate backing the event once you access the field with reflection.
Edit: this falls prey to the same problem, but is better than getting it as a field and casting it.
var eventInfo = currentType.GetEvent(eventName);
var eventRaiseMethod = eventInfo.GetRaiseMethod()
eventRaiseMethod.Invoke()
Raising event via reflection using webforms with codebehind
The problem I was having was that iterating through the field collection of Me.GetType()
wasn't returning the "TestEvent" field. I did some further digging and realised that that is because the event is declared in the "code behind" class, e.g. myPageName.aspx.vb, however, during runtime, this code was being called from the inheriting "design" class, e.g. myPageName.aspx.
This blog post pointed out that even with BindingFlags.FlattenHierarchy
, .GetType.GetField()
will not return private static fields from inherited classes: https://web.archive.org/web/20131213074318/http://bobpowell.net/eventsubscribers.aspx
As a result, the solution was to use Me.GetType.BaseType.GetField("TestEvent")
, and from there use the technique as described by Tejas Sharma. A VB.NET example of this technique is offered in this answer: How to Attach the Events of an Original Object to a Deep Copied Clone
How would you invoke an event with reflection?
Exactly what you want - taken from this fantastic page....
public void TriggerEvent(string handler, EventArgs e)
{
MulticastDelegate eventDelegate =
(MulticastDelegate)this.GetType().GetField(handler,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).GetValue(this);
Delegate[] delegates = eventDelegate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, new object[] { this, e });
}
}
C# Reflection: How to invoke a EventInfo?
"Invoking" event only makes sense if event is implemented without overriding add\remove:
class Test {
public event Action TestEvent;
void Invoke() {
// fine
TestEvent();
}
}
However, concept of invoking for event with custom add\remove does not make sense:
class Test {
public event Action TestEvent
{
add { }
remove { }
}
void Invoke() {
// does not compile, invoke what?
TestEvent();
}
}
So event invocation is just syntax sugar of calling underlying compiler-generated delegate field, for events with "default" implementation.
Knowing that, you can search for that field and invoke it. This is a private field with the same name as event:
class Program {
static void Main(string[] args) {
var test = new Test();
test.TestEvent += OnTest;
var backingField = typeof(Test).GetField("TestEvent", BindingFlags.Instance | BindingFlags.NonPublic);
var delegateInstance = (Action)backingField.GetValue(test);
delegateInstance();
}
private static void OnTest() {
Console.WriteLine("Event invoked");
}
}
class Test {
public event Action TestEvent;
}
That said, compilers of some .NET languages might generate "Raise" method for autoimplemented event. If that is the case - GetRaiseMethod
will return such method. C# compiler doesn't do that though. So if you want to be safe - you might first call GetRaiseEvent
and if it returns null - fallback to field approach. Of course you should expect field to be null also (since not all events are invocable as described above - there is not requirement for such field to exist).
AddEventHandler using reflection
Here's a sample showing how to attach an event using reflection:
class Program
{
static void Main(string[] args)
{
var p = new Program();
var eventInfo = p.GetType().GetEvent("TestEvent");
var methodInfo = p.GetType().GetMethod("TestMethod");
Delegate handler =
Delegate.CreateDelegate(eventInfo.EventHandlerType,
p,
methodInfo);
eventInfo.AddEventHandler(p, handler);
p.Test();
}
public event Func<string> TestEvent;
public string TestMethod()
{
return "Hello World";
}
public void Test()
{
if (TestEvent != null)
{
Console.WriteLine(TestEvent());
}
}
}
Subscribing an Action to any event type via reflection
static void AddEventHandler(EventInfo eventInfo, object item, Action action)
{
var parameters = eventInfo.EventHandlerType
.GetMethod("Invoke")
.GetParameters()
.Select(parameter => Expression.Parameter(parameter.ParameterType))
.ToArray();
var handler = Expression.Lambda(
eventInfo.EventHandlerType,
Expression.Call(Expression.Constant(action), "Invoke", Type.EmptyTypes),
parameters
)
.Compile();
eventInfo.AddEventHandler(item, handler);
}
static void AddEventHandler(EventInfo eventInfo, object item, Action<object, EventArgs> action)
{
var parameters = eventInfo.EventHandlerType
.GetMethod("Invoke")
.GetParameters()
.Select(parameter => Expression.Parameter(parameter.ParameterType))
.ToArray();
var invoke = action.GetType().GetMethod("Invoke");
var handler = Expression.Lambda(
eventInfo.EventHandlerType,
Expression.Call(Expression.Constant(action), invoke, parameters[0], parameters[1]),
parameters
)
.Compile();
eventInfo.AddEventHandler(item, handler);
}
Usage:
Action action = () => BM_21_Grad.LaunchMissle();
foreach (var eventInfo in form.GetType().GetEvents())
{
AddEventHandler(eventInfo, form, action);
}
Subscribe to an event with Reflection
With a few changes, I was able to execute your sample.
Firstly, the method on Trace
must have a different signature to correspond with the EventHandler
type:
public class Trace
{
public void WriteTrace(object sender, EventArgs e)
{
Console.WriteLine("Trace !");
}
}
Next, a few changes were made to the SubscribeEvent
:
public void SubscribeEvent(Control control)
{
if (typeof(Control).IsAssignableFrom(control.GetType()))
{
Trace test = this;
MethodInfo method = typeof(Trace).GetMethod("WriteTrace");
EventInfo eventInfo = control.GetType().GetEvent("Load");
// Create the delegate on the test class because that's where the
// method is. This corresponds with `new EventHandler(test.WriteTrace)`.
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, test, method);
// Assign the eventhandler. This corresponds with `control.Load += ...`.
eventInfo.AddEventHandler(control, handler);
}
}
I hope this helps.
Related Topics
How Do C# Events Work Behind the Scenes
How to Provide Custom Cast Support for My Class
Illustrating Usage of the Volatile Keyword in C#
Pattern for Calling Wcf Service Using Async/Await
Detecting Network Connection Speed and Bandwidth Usage in C#
How to Get the Width and Height of a Multi-Dimensional Array
Convert String to Hex-String in C#
Mvvm Light 5.0: How to Use the Navigation Service
Linq Order by Null Column Where Order Is Ascending and Nulls Should Be Last
Dependency Injection Using Azure Webjobs Sdk
Copy Rows from One Datatable to Another Datatable
Jquery Ajax Call to Httpget Webmethod (C#) Not Working
Yield Statement Implementation
Change Border Color in Textbox C#