Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Binding to C# Events Using Reflection

Tags:

c#

events

I have a WPF control with very poor documentation.

In the codebehind I'd like to reflect over the events that the control fires using GetType().GetEVents() and add a handler to each one which simply prints out the name of the event.

This would allow me to see what interacting with the control is actually doing.

So far I have:

foreach (var e in GetType().GetEvents())
{
    var name = e.Name;
    var handler = new Action<object,object>( (o1,o2) =>Console.WriteLine(name));

    try
    {
        e.AddEventHandler(
                     this,
                     Delegate.CreateDelegate(
                               e.EventHandlerType,
                               handler.Target, 
                               handler.Method
                               ));
    }
    catch (Exception ex)
    {
        Console.WriteLine( "Failed to bind to event {0}", e.Name);
    }
}

Which seems to work when the event signature is (object,EventArgs) but fails to bind when on certain other events.

Is there a way to do this without necessarily knowing the signature of the event?

like image 956
Nick Avatar asked Oct 26 '12 08:10

Nick


People also ask

What type of binding does C use?

Function Call Binding This type of binding is done during compilation by the compiler and linker. The compiler has full knowledge of the function definition and the places where this function is called in the program. The C language typically uses this type of binding.

What is static and dynamic binding in C?

n static binding, function calls are resolved at compile time by the compiler itself. The binding of all the static and private functions/methods of a class happens at compile time. In dynamic binding, function calls are resolved at run time. Function overriding in OOP is possible due to dynamic/late binding.

Does C support static binding?

Yes, it is possible (almost everything is possible with C ) however it is usually not covenient, C++ is better suited for such a job.

What is mean by dynamic binding in C++?

By default, C++ matches a function call with the correct function definition at compile time. This is called static binding. You can specify that the compiler match a function call with the correct function definition at runtime; this is called dynamic binding.


2 Answers

Look at the example found here: http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx

They use the delegate's Invoke-method to get the signature.

like image 40
Davio Avatar answered Sep 20 '22 14:09

Davio


You could use the System.Linq.Expressions.Expression class to generate dynamic handlers matching the signature of the event - into which you simply place a call to Console.WriteLine.

The Expression.Lambda method (have provided a link to the specific overload you'd need) can be used to generate a Func<> or, more likely, Action<> of the correct type.

You reflect the delegate type of the event (grabbing it's Invoke method as mentioned by @Davio) to pull out all the arguments and create ParameterExpressions for each of those to supply to the lambda method.

Here's a complete solution that you can paste into a standard unit test, I'll explain afterwards in a follow-up edit:

public class TestWithEvents
{
  //just using random delegate signatures here
  public event Action Handler1;
  public event Action<int, string> Handler2;

  public void RaiseEvents(){
    if(Handler1 != null)
        Handler1();
    if(Handler2 != null)
      Handler2(0, "hello world");
  }
}

public static class DynamicEventBinder
{
  public static Delegate GetHandler(System.Reflection.EventInfo ev) {
    string name = ev.Name;
    // create an array of ParameterExpressions
    // to pass to the Expression.Lambda method so we generate
    // a handler method with the correct signature.
    var parameters = ev.EventHandlerType.GetMethod("Invoke").GetParameters().
      Select((p, i) => Expression.Parameter(p.ParameterType, "p" + i)).ToArray();

    // this and the Compile() can be turned into a one-liner, I'm just
    // splitting it here so you can see the lambda code in the Console
    // Note that we use the Event's type for the lambda, so it's tightly bound
    // to that event.
    var lambda = Expression.Lambda(ev.EventHandlerType,
      Expression.Call(typeof(Console).GetMethod(
        "WriteLine",
        BindingFlags.Public | BindingFlags.Static,
        null,
        new[] { typeof(string) },
        null), Expression.Constant(name + " was fired!")), parameters);

    //spit the lambda out (for bragging rights)
    Console.WriteLine(
      "Compiling dynamic lambda {0} for event \"{1}\"", lambda, name);
    return lambda.Compile();
  }

  //note - an unsubscribe might be handy - which would mean
  //caching all the events that were subscribed for this object
  //and the handler.  Probably makes more sense to turn this type
  //into an instance type that binds to a single object...
  public static void SubscribeAllEvents(object o){
    foreach(var e in o.GetType().GetEvents())
    {
      e.AddEventHandler(o, GetHandler(e));
    }
  }
}

[TestMethod]
public void TestSubscribe()
{
  TestWithEvents testObj = new TestWithEvents();
  DynamicEventBinder.SubscribeAllEvents(testObj);
  Console.WriteLine("Raising events...");
  testObj.RaiseEvents();
  //check the console output
}

An outline - we start with a type that has some events (I'm using Action but it should work with anything), and has a method that we can use to test-fire all those events that have subscribers.

then to the DynamicEventBinder class, which has two methods: GetHandler - to get a handler for a particular event for a particular type; and SubscribeAllEvents which binds all those events for a given instance of that type - which simply loops over all the events, calling AddEventHandler for each, calling GetHandler to get the handler.

The GetHandler method is where the meat and bones is - and does exactly as I suggest in the outline.

A delegate type has an Invoke member compiled into it by the compiler which mirrors the signature of any handler that it can be bound to. So, we reflect that method and get any parameters it has, creating Linq ParameterExpression instances for each. Naming the parameter is a nicety, it's the type here that matters.

Then we build a single-line lambda, whose body is basically:

 Console.WriteLine("[event_name] was fired!");

(Note here that the event's name is pulled into the dynamic code and incorporated into a constant string as far as the code is concerned)

When we build the lambda, we also tell the Expression.Lambda method the type of delegate we intend to build (bound directly to the delegate type of the event), and by passing the ParameterExpression array that we created before, it will generate a method which has that many parameters. We use the Compile method to actually compile the dynamic code, which gives us a Delegate which we can then use as an argument to AddEventHandler.

I sincerely hope this explains what we've done - if you've not worked with Expressions and dynamic code before it can be mind-bending stuff. Indeed, some of the people I work with simply call this voodoo.

like image 195
Andras Zoltan Avatar answered Sep 17 '22 14:09

Andras Zoltan