Events and Expressions
I ran into a scenario recently where I had a C# Action
(That is, void params, and void return), and needed to get it to execute
when an event
was triggered. Any event. With any number of parameters.
The way I solved this problem is using the LINQ Compiled Expression Trees. These little things are useful when you need to write a lambda/closure, and don't necessarily know what paramater types, or calls, will be passed in beforehand.
The Problem
Here, fundamentally, is what I was trying to solve:
public static void AddHandler(object instance, EventInfo eventInfo, Action action)
{
eventInfo.AddEventHandler (instance, action); //Fit square square peg into round hole
}
The problem here is that I want to bind an action to an event, but don't know what the event delegate type is.
Why do this
In my case, it was part of a helper method for my UI library. I wanted someone to be able to bind to an event on a widget, but without knowing the exact parameters that event took ahead of time. This was purely sugar-coating.
Expressions to the Rescue
I ended up solving this by compiling an expression during runtime that takes the exact parameters of the event,
and then wrapping the call to the Action
.
Here's the code:
public static class AnonymousAction
{
public static Delegate WrapDelegate(Action action, Type targetType)
{
var invoke = targetType.GetMethod ("Invoke");
if (invoke == null)
throw new ArgumentException ("ofType must be delegate");
var parameters = invoke.GetParameters ();
var expressionParams = new ParameterExpression[parameters.Length];
for (int i=0; i<parameters.Length; ++i)
{
expressionParams [i] = Expression.Parameter (parameters [i].ParameterType);
}
var call = Expression.Call (
Expression.Constant(action),
typeof(Action).GetMethod ("Invoke")
);
return Expression.Lambda (targetType, call, expressionParams).Compile ();
}
}
public class MyReceiver
{
public event Action<MyReceiver, int> OnAction;
public void Do()
{
OnAction (this, 22);
}
}
public class Test
{
public void Run()
{
Action onAction = () => {
Console.WriteLine ("Did something");
};
var receiver = new MyReceiver ();
var evt = receiver.GetType ().GetEvent ("OnAction");
evt.AddEventHandler (receiver, AnonymousAction.WrapDelegate (onAction, evt.EventHandlerType));
receiver.Do ();
}
}
static void Main()
{
var t = new Test ();
t.Run ();
}