Reputation: 2882
So I have an event declared as follows:
public event EventHandler OnChangeDetected;
Then I have the following handler that gets assigned to that event.
myObject.OnChangeDetected += OnTableChanged;
My understanding is that this type of event would require that my OnTableChanged method to have the following signature which compiles just fine.
public void OnTableChanged(object sender, EventArgs e)
Now I want to replace the OnTableChanged event to the following signature.
public void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)
However, when I replace the parameters with derived types, it complains that there is no overload for "OnTableChanged" that matches delegate EventHandler. Since SqlChangeNotifier derives from Object, and SqlNotificationEventArgs derives from EventArgs, can anyone explain why I cannot have these derived parameters types since they inherit from the correct base types?
Upvotes: 0
Views: 1515
Reputation: 10708
For the overall topic on this sort of change, see Contravariance and Covariance.
Keep in mind that event systems are just fancy ways of calling a series of method which you can swap out at runtime, so if you can't call the handler directly with the exact same arguments you pass to raise the event, then the event system can't do it either.
Like methods, event handlers are Contravariant, meaning that a delagate of type EventHandler<SpecializedEventArgs>
(assuming SpecializedEventArgs : EventArgs
) will accept a handler of signature public void Handler(object sender, EventArgs args)
because the call to the event ends up calling the handler with a SpecializedEventArgs
object, which can be implicitly converted to EventArgs
by simple polymorphism. IE the following will compile:
public static event EventHandler<SpecializedEventArgs> Event;
public static void Handler(object sender, EventArgs args) { }
public static void Main() {
Event += Handler;
//...
Upvotes: 0
Reputation: 43495
You can't do that because the subscriber of the event should get a derived instance, but the publisher can only provide a base instance.
You are allowed to do the opposite though:
public static event KeyEventHandler ChangeDetected; // handler with derived args signature
private static void Program_ChangeDetected(object sender, EventArgs e) // base event args are OK
{
Console.WriteLine("ChangeDetected");
}
static void Main(string[] args)
{
ChangeDetected += Program_ChangeDetected;
ChangeDetected?.Invoke(null, new KeyEventArgs(default)); // base event args are NOT OK
}
Upvotes: 0
Reputation: 387647
EventHandler
is a delegate of the type void EventHandler(object sender, EventArgs e)
. So the signature of the handlers that subscribe to the event has to match this.
Now void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)
is more specific than that: It can no longer take any sender
object and the event arguments also have to be of type SqlNotificationEventArgs
.
The problem is now that when an event is raised, the original sender of the event will try to call the event handlers with the arguments object sender, EventArgs e
but your method requires more specialized types. There is no guarantee from the type system that these arguments are in fact of those specialized types.
If you want to require these types, you will need to change the type of your event to a more restrictive delegate type.
Upvotes: 2