2

RELATED


I need to detect when an event is fired. To do so I am trying to subscribe dynamically on the event.

The problem is that I have different types of delegates, not all events have the same signature. The solutions provided in here and here expects object sender, EventArgs e, which I am not using, so I get an exception telling the types doesn't match.

Here are some examples of a delegates I have:

public delegate void OnEventA(int id);
public delegate void OnEventB(double num, string name);

How can I create the correct delegate?

Community
  • 1
  • 1
BrunoLM
  • 97,872
  • 84
  • 296
  • 452

2 Answers2

6

EventInfo has a method AddEventHandler that you can use. If you don't have a delegate instance, then you can create a delegate dynamically using Delegate.CreateDelegate:

var eh = Delegate.CreateDelegate(ei.EventHandlerType, target, methodInfo);
ei.AddEventHandler(owner, eh);

In this example target is the target object for the delegate and methodInfo is a MethodInfo of a method in the target object. Finally, owner is the object where the event ei belongs.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Do I need MethodInfo? Can't I use a lambda? What if I need to pass parameters? – BrunoLM Sep 22 '10 at 18:28
  • @BrunoLM: You can use lambda in place of `eh` if you know the delegate type (e.g. `EventHandler`). If you don't know this, you'll need to create a new instance of some (your) object, use it in place of `target` and pass parameters to this object in constructor (in this case, method info would be info about some method in this object - you can e.g. define an `Invoke` method). – Tomas Petricek Sep 22 '10 at 18:36
  • More specifically: You could try `OnLoad eh = () => { ... }`. It will work as long as all `Tickable` events are of a delegate type `OnLoad`. – Tomas Petricek Sep 22 '10 at 18:38
  • What if the delegate is different in each case? And what if it have parameters? – BrunoLM Sep 23 '10 at 12:52
  • If the delegate is different then you need to create it dynamically using `CreateDelegate`. To provide some parameters, you'll need to create a new object in place of `target`. – Tomas Petricek Sep 23 '10 at 14:30
3

After some research I found some articles:

It helped me to understand what I was trying to do and I should do.

I need to use Delegate.CreateDelegate passing the EventHandlerType (the type of the event, the delegate), a instance of a class and the method info of the method (from the class in the previous parameter) that will handle the event. Target is the control that fires this event.

Delegate handler = Delegate.CreateDelegate(evt.EventHandlerType, abc, mi1, false);
evt.AddEventHandler(target, handler);

Further digging lead me to this method. I can subscribe to events using lambda expression. Using Action<T> I can subscribe with different types and numbers of parameters.

public static Delegate Create<T>(EventInfo e, Action<T> a)
{
    var parameters = e.EventHandlerType.GetMethod("Invoke").GetParameters().Select(p => Expression.Parameter(p.ParameterType, "p")).ToArray();
    var exp = Expression.Call(Expression.Constant(a), a.GetType().GetMethod("Invoke"), parameters);
    var l = Expression.Lambda(exp, parameters);
    return Delegate.CreateDelegate(e.EventHandlerType, l.Compile(), "Invoke", false);
}

Using this method (e is the EventInfo; EventManager is the class with the static method above)

e.AddEventHandler(this, EventManager.Create<int>(e, (x) => Console.WriteLine("Execute")));
Community
  • 1
  • 1
BrunoLM
  • 97,872
  • 84
  • 296
  • 452