When learning more about the standard event model in .NET, I found that before introducing generics in C#, the method that will handle an event is represented by this delegate t
First off, to address some concerns in the comments to the question: I generally push back hard on "why not" questions because it's hard to find concise reasons why everyone in the world chose to not do this work, and because all work is not done by default. Rather, you have to find a reason to do work, and take away resources from other work that is less important to do it.
Moreover, "why not" questions of this form, which ask about the motivations and choices of people who work at a particular company may only be answerable by the people who made that decision, who are probably not around here.
However, in this case we can make an exception to my general rule of closing "why not" questions because the question illustrates an important point about delegate covariance that I have never written about before.
I did not make the decision to keep event delegates non-variant, but had I been in a position to do so, I would have kept event delegates non-variant, for two reasons.
The first is purely an "encourage good practices" point. Event handlers are usually purpose-built for handling a particular event, and there is no good reason I'm aware of to make it easier than it already is to use delegates that have mismatches in the signature as handlers, even if those mismatches can be dealt with through variance. An event handler that matches exactly in every respect the event it is supposed to be handling gives me more confidence that the developer knows what they're doing when constructing an event-driven workflow.
That's a pretty weak reason. The stronger reason is also the sadder reason.
As we know, generic delegate types can be made covariant in their return types and contravariant in their parameter types; we normally think of variance in the context of assignment compatibility. That is, if we have a Func
in hand, we can assign it to a variable of type Func
and know that the underlying function will always take a mammal -- because now it will only get giraffes -- and will always return an animal -- because it returns mammals.
But we also know that delegates may be added together; delegates are immutable, so adding two delegates together produces a third; the sum is the sequential composition of the summands.
Field-like events are implemented using delegate summation; that's why adding a handler to an event is represented as +=
. (I am not a big fan of this syntax, but we're stuck with it now.)
Though both these features work well independently of each other, they work poorly in combination. When I implemented delegate variance, our tests discovered in short order that there were a number of bugs in the CLR regarding delegate addition where the underlying delegate types were mismatched due to variance-enabled conversions. These bugs had been there since CLR 2.0, but until C# 4.0, no mainstream language had ever exposed the bugs, no test cases had been written for them, and so on.
Sadly, I do not recall what the reproducers for the bugs were; it was twelve years ago and I do not know if I still have any notes on it tucked away on a disk somewhere.
We worked with the CLR team at the time to try and get these bugs addressed for the next version of the CLR, but they were not considered high enough priority compared to their risk. Lots of types like IEnumerable
and IComparable
and so on were made variant in those releases, as were the Func
and Action
types, but it is rare to add together two mismatched Func
s using a variant conversion. But for event delegates, their only purpose in life is to be added together; they would be added together all the time, and had they been variant, there would have been risk of exposing these bugs to a great many users.
I lost track of the issues shortly after C# 4 and I honestly do not know if they were ever addressed. Try adding together some mismatched delegates in various combinations and see if anything bad happens!
So that's a good but unfortunate reason why to not make event delegates variant in the C# 4.0 release timeframe. Whether there is still a good reason, I don't know. You'd have to ask someone on the CLR team.