Geekn
Geekn

Reputation: 2882

Cannot assign event handler that uses derived type for EventArgs in c#

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

Answers (3)

David
David

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

Theodor Zoulias
Theodor Zoulias

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

poke
poke

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

Related Questions