views:

122

answers:

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?

+5  A: 

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
Do I need MethodInfo? Can't I use a lambda? What if I need to pass parameters?
BrunoLM
@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
More specifically: You could try `OnLoad eh = () => { ... }`. It will work as long as all `Tickable` events are of a delegate type `OnLoad`.
Tomas Petricek
What if the delegate is different in each case? And what if it have parameters?
BrunoLM
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
+1  A: 

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")));
BrunoLM