Good day! I want to understand the following issue:
Let's say that we have simple EventSubscriber class
public class EventSubscriber
{
public static Delegate AddEventHandler(object target, string eventName, Action<object, EventArgs> action)
{
EventInfo eventInfo = target.GetType().GetEvent(eventName);
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, action.Method);
eventInfo.AddEventHandler(target, handler);
return handler;
}
public static void RemoveEventHandler(object target, string eventName, Delegate handler)
{
var eventInfo = target.GetType().GetEvent(eventName);
eventInfo.RemoveEventHandler(target, handler);
}
}
And let's say that we have timer which we want subscribe on Elapsed event.
class Program
{
static System.Timers.Timer timer;
public static void InitTimer(int interval)
{
timer = new System.Timers.Timer(interval);
timer.Start();
}
static void Main(string[] args)
{
int interval = 1000;
InitTimer(interval);
var handler = EventSubscriber.AddEventHandler(timer, "Elapsed", Handler);
Thread.Sleep(Convert.ToInt16(interval * 5));
EventSubscriber.RemoveEventHandler(timer, "Elapsed", handler);
Thread.Sleep(Convert.ToInt16(interval * 5));
}
public static void Handler(object sender, EventArgs args)
{
Console.WriteLine("BOOO");
}
}
Now if you compile this application everything will work fine and you will see extected behaviour of application. Let's wrap our timer in class:
public class TimerWrapper
{
public System.Timers.Timer Timer { get; set; }
public int Interval { get; set; }
public TimerWrapper(int interval)
{
Interval = interval;
Timer = new System.Timers.Timer(Interval);
Timer.Start();
}
public static void Handler(object sender, EventArgs args)
{
Console.WriteLine("BOOO");
}
}
Now let's check it out:
class Program
{
static void Main(string[] args)
{
int interval = 1000;
TimerWrapper timerWrapper = new TimerWrapper(interval);
var handler = EventSubscriber.AddEventHandler(timerWrapper.Timer, "Elapsed", TimerWrapper.Handler);
Thread.Sleep(Convert.ToInt16(interval * 5));
EventSubscriber.RemoveEventHandler(timerWrapper.Timer, "Elapsed", handler);
Thread.Sleep(Convert.ToInt16(interval * 5));
}
}
Everything works as in previous time. But what if we will make our handler in TimerWrapper non static and run application? We will recieve System.ArgumentException with next message "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."
I have assumptions why it's so, but I want know it firmly.
I hope to get comprehensive answer, thanks in advance and thanks for your time!
The problem is that you're calling Delegate.CreateDelegate
with an overload which is expected to work for static methods (or for instance methods where there's an extra delegate parameter which is the target of the method call).
All you need to do to get it to work is pass in the target of the existing delegate when you create the new one:
Delegate handler = Delegate.CreateDelegate(
eventInfo.EventHandlerType,
action.Target,
action.Method);
If the original delegate (action
) is using a static method, action.Target
will already be null
, so that's fine.
See more on this question at Stackoverflow