Article Image
read

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 ();
}
Blog Logo

Christopher LaPointe


Published

Image

Chris LaPointe

Another site of Code

Back to Overview