Why wasn't TEventArgs made contravariant in the standard event pattern in the .NET ecosystem?Is there a...

What is the purpose of easy combat scenarios that don't need resource expenditure?

How should I handle players who ignore the session zero agreement?

Increment each digit in a number to form a new number

Why is it that Bernie Sanders is always called a "socialist"?

What sets the resolution of an analog resistive sensor?

Cookies - Should the toggles be on?

Words and Words with "ver-" Prefix

What would the chemical name be for C13H8Cl3NO

Slow While Loop, Query Improvment Assistance

Making him into a bully (how to show mild violence)

Why am I able to open Wireshark in macOS without root privileges?

How much mayhem could I cause as a sentient fish?

How to use Mathematica to do a complex integrate with poles in real axis?

Do authors have to be politically correct in article-writing?

Can my spouse sponsor me for a UK visa if I am unemployed?

Is it a fallacy if someone claims they need an explanation for every word of your argument to the point where they don't understand common terms?

Is there any risk in sharing info about technologies and products we use with a supplier?

A starship is travelling at 0.9c and collides with a small rock. Will it leave a clean hole through, or will more happen?

In mixed effect models, how account for grouped random effects?

What is the proper way to reproach a rav?

How to tell if a BJT is PNP or NPN by looking at the circuit?

How do you funnel food off a cutting board?

Consequences of lack of rigour

What are the exceptions to Natural Selection?



Why wasn't TEventArgs made contravariant in the standard event pattern in the .NET ecosystem?


Is there a reason for C#'s reuse of the variable in a foreach?Why is typeA == typeB slower than typeA == typeof(TypeB)?Why the interface IOrderedEnumerable<T> isn't covariant in T?Event Signature in .NET — Using a Strong Typed 'Sender'?Event and delegate contravariance in .NET 4.0 and C# 4.0Is there a way to determine the Variance of an Interface / Delegate in C# 4.0?Contra- and Co-variance - CLR via C#Contravariance problems with event propertiesWhy generic interfaces are not co/contravariant by default?Why events does not support binding inherited types?Does normal object base type - derived type conversion come under Covariance and contravariance?Is it a mistake in msdn documentation for covariance at delegates?What is the difference between .NET Core and .NET Standard Class Library project types?













10















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 type:



//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);


But after generics were introduced in C# 2, I think this delegate type was rewritten using genericity:



//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains the event data.
//
// Type parameters:
// TEventArgs:
// The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);


I have two questions here:



First, why wasn't the TEventArgs type parameter made contravariant ?



If I'm not mistaken it is recommended to make the type parameters that appear as formal parameters in a delegate's signature contravariant and the type parameter that will be the return type in the delegate signature covariant.



In Joseph Albahari's book, C# in a Nutshell, I quote:




If you’re defining a generic delegate type, it’s good practice to:




  • Mark a type parameter used only on the return value as covariant (out).

  • Mark any type parameters used only on parameters as contravariant (in).


Doing so allows conversions to work naturally by respecting
inheritance relationships between types.




Second question: Why was there no generic constraint to enforce that the TEventArgs derive from System.EventArgs?



As follows:



public delegate void EventHandler<TEventArgs>  (object source, TEventArgs e) where TEventArgs : EventArgs; 


Thanks in advance.



Edited to clarify the second question:



It seems like the generic constraint on TEventArgs (where TEventArgs : EventArgs) was there before and it was removed by Microsoft, so seemingly the design team realized that it didn’t make much practical sense.



I edited my answer to include some of the screenshots from



.NET reference source



enter image description here










share|improve this question




















  • 3





    I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

    – Zack ISSOIR
    7 hours ago






  • 11





    @CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

    – Rob
    7 hours ago








  • 3





    @CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

    – Zack ISSOIR
    7 hours ago






  • 3





    Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

    – Eric Lippert
    6 hours ago






  • 3





    I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

    – Eric Lippert
    5 hours ago
















10















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 type:



//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);


But after generics were introduced in C# 2, I think this delegate type was rewritten using genericity:



//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains the event data.
//
// Type parameters:
// TEventArgs:
// The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);


I have two questions here:



First, why wasn't the TEventArgs type parameter made contravariant ?



If I'm not mistaken it is recommended to make the type parameters that appear as formal parameters in a delegate's signature contravariant and the type parameter that will be the return type in the delegate signature covariant.



In Joseph Albahari's book, C# in a Nutshell, I quote:




If you’re defining a generic delegate type, it’s good practice to:




  • Mark a type parameter used only on the return value as covariant (out).

  • Mark any type parameters used only on parameters as contravariant (in).


Doing so allows conversions to work naturally by respecting
inheritance relationships between types.




Second question: Why was there no generic constraint to enforce that the TEventArgs derive from System.EventArgs?



As follows:



public delegate void EventHandler<TEventArgs>  (object source, TEventArgs e) where TEventArgs : EventArgs; 


Thanks in advance.



Edited to clarify the second question:



It seems like the generic constraint on TEventArgs (where TEventArgs : EventArgs) was there before and it was removed by Microsoft, so seemingly the design team realized that it didn’t make much practical sense.



I edited my answer to include some of the screenshots from



.NET reference source



enter image description here










share|improve this question




















  • 3





    I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

    – Zack ISSOIR
    7 hours ago






  • 11





    @CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

    – Rob
    7 hours ago








  • 3





    @CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

    – Zack ISSOIR
    7 hours ago






  • 3





    Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

    – Eric Lippert
    6 hours ago






  • 3





    I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

    – Eric Lippert
    5 hours ago














10












10








10


6






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 type:



//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);


But after generics were introduced in C# 2, I think this delegate type was rewritten using genericity:



//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains the event data.
//
// Type parameters:
// TEventArgs:
// The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);


I have two questions here:



First, why wasn't the TEventArgs type parameter made contravariant ?



If I'm not mistaken it is recommended to make the type parameters that appear as formal parameters in a delegate's signature contravariant and the type parameter that will be the return type in the delegate signature covariant.



In Joseph Albahari's book, C# in a Nutshell, I quote:




If you’re defining a generic delegate type, it’s good practice to:




  • Mark a type parameter used only on the return value as covariant (out).

  • Mark any type parameters used only on parameters as contravariant (in).


Doing so allows conversions to work naturally by respecting
inheritance relationships between types.




Second question: Why was there no generic constraint to enforce that the TEventArgs derive from System.EventArgs?



As follows:



public delegate void EventHandler<TEventArgs>  (object source, TEventArgs e) where TEventArgs : EventArgs; 


Thanks in advance.



Edited to clarify the second question:



It seems like the generic constraint on TEventArgs (where TEventArgs : EventArgs) was there before and it was removed by Microsoft, so seemingly the design team realized that it didn’t make much practical sense.



I edited my answer to include some of the screenshots from



.NET reference source



enter image description here










share|improve this question
















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 type:



//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);


But after generics were introduced in C# 2, I think this delegate type was rewritten using genericity:



//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains the event data.
//
// Type parameters:
// TEventArgs:
// The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);


I have two questions here:



First, why wasn't the TEventArgs type parameter made contravariant ?



If I'm not mistaken it is recommended to make the type parameters that appear as formal parameters in a delegate's signature contravariant and the type parameter that will be the return type in the delegate signature covariant.



In Joseph Albahari's book, C# in a Nutshell, I quote:




If you’re defining a generic delegate type, it’s good practice to:




  • Mark a type parameter used only on the return value as covariant (out).

  • Mark any type parameters used only on parameters as contravariant (in).


Doing so allows conversions to work naturally by respecting
inheritance relationships between types.




Second question: Why was there no generic constraint to enforce that the TEventArgs derive from System.EventArgs?



As follows:



public delegate void EventHandler<TEventArgs>  (object source, TEventArgs e) where TEventArgs : EventArgs; 


Thanks in advance.



Edited to clarify the second question:



It seems like the generic constraint on TEventArgs (where TEventArgs : EventArgs) was there before and it was removed by Microsoft, so seemingly the design team realized that it didn’t make much practical sense.



I edited my answer to include some of the screenshots from



.NET reference source



enter image description here







c# .net .net-core contravariance






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 15 mins ago









Boann

37.1k1290121




37.1k1290121










asked 7 hours ago









Zack ISSOIRZack ISSOIR

962618




962618








  • 3





    I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

    – Zack ISSOIR
    7 hours ago






  • 11





    @CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

    – Rob
    7 hours ago








  • 3





    @CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

    – Zack ISSOIR
    7 hours ago






  • 3





    Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

    – Eric Lippert
    6 hours ago






  • 3





    I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

    – Eric Lippert
    5 hours ago














  • 3





    I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

    – Zack ISSOIR
    7 hours ago






  • 11





    @CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

    – Rob
    7 hours ago








  • 3





    @CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

    – Zack ISSOIR
    7 hours ago






  • 3





    Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

    – Eric Lippert
    6 hours ago






  • 3





    I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

    – Eric Lippert
    5 hours ago








3




3





I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

– Zack ISSOIR
7 hours ago





I don't think it's fair to close this question. I presented all the elements that make my question legal and verifiable. There must be a very strong reason to vote for closing it ...

– Zack ISSOIR
7 hours ago




11




11





@CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

– Rob
7 hours ago







@CamiloTerevinto, I've not encountered that specific rule before. If that was the case then this answer, to the question Is there a reason for C#'s reuse of the variable in a foreach?, written by Eric Lippert (who worked on the C# compiler at Microsoft) stackoverflow.com/a/8899347/7872 would've been missed out on. "Not everyone has the knowledge to answer this question" is a quite awful reason to close it.

– Rob
7 hours ago






3




3





@CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

– Zack ISSOIR
7 hours ago





@CamiloTerevinto Look I understand your point of view even if I disagree with it. I already asked a question like this befoe and I had an answer , have a look at this So question stackoverflow.com/questions/47728857/…

– Zack ISSOIR
7 hours ago




3




3





Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

– Eric Lippert
6 hours ago





Minor correction to your timeline: generics were introduced in C# 2. Supporting variance on generics was introduced in that version of the CLR, but C# did not take advantage of it until C# 4.

– Eric Lippert
6 hours ago




3




3





I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

– Eric Lippert
5 hours ago





I disagree that this question is "primarily opinion based". The question does not solicit an opinon; it solicits the arguments made to justify a design decision. We can say that the question is vague because "why not" questions are inherently vague, and we can say that likely no one on SO has direct knowledge of those arguments, but I don't think we can say that either of those things are soliciting opinions. I've voted to re-open.

– Eric Lippert
5 hours ago












1 Answer
1






active

oldest

votes


















23














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<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> 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<T> and IComparable<T> 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 Funcs 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.






share|improve this answer





















  • 4





    Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

    – Zack ISSOIR
    5 hours ago






  • 1





    "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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

    – Dai
    4 hours ago






  • 4





    @Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

    – Eric Lippert
    3 hours ago











  • There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

    – Aaron M. Eshbach
    21 mins ago













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54907236%2fwhy-wasnt-teventargs-made-contravariant-in-the-standard-event-pattern-in-the-n%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









23














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<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> 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<T> and IComparable<T> 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 Funcs 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.






share|improve this answer





















  • 4





    Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

    – Zack ISSOIR
    5 hours ago






  • 1





    "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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

    – Dai
    4 hours ago






  • 4





    @Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

    – Eric Lippert
    3 hours ago











  • There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

    – Aaron M. Eshbach
    21 mins ago


















23














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<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> 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<T> and IComparable<T> 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 Funcs 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.






share|improve this answer





















  • 4





    Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

    – Zack ISSOIR
    5 hours ago






  • 1





    "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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

    – Dai
    4 hours ago






  • 4





    @Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

    – Eric Lippert
    3 hours ago











  • There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

    – Aaron M. Eshbach
    21 mins ago
















23












23








23







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<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> 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<T> and IComparable<T> 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 Funcs 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.






share|improve this answer















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<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> 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<T> and IComparable<T> 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 Funcs 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.







share|improve this answer














share|improve this answer



share|improve this answer








edited 46 mins ago









Wai Ha Lee

5,956123964




5,956123964










answered 6 hours ago









Eric LippertEric Lippert

542k14610621947




542k14610621947








  • 4





    Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

    – Zack ISSOIR
    5 hours ago






  • 1





    "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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

    – Dai
    4 hours ago






  • 4





    @Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

    – Eric Lippert
    3 hours ago











  • There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

    – Aaron M. Eshbach
    21 mins ago
















  • 4





    Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

    – Zack ISSOIR
    5 hours ago






  • 1





    "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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

    – Dai
    4 hours ago






  • 4





    @Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

    – Eric Lippert
    3 hours ago











  • There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

    – Aaron M. Eshbach
    21 mins ago










4




4





Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

– Zack ISSOIR
5 hours ago





Thank you very much, Eric, for answering this question. I read your very great and well-explained blog posts on variance and started looking around in the .NET framework until I found the examples of event delegates that this post is about. I think that this great and convincing answer completes those blog posts, as there is no article that discusses the points you mentioned in this answer.

– Zack ISSOIR
5 hours ago




1




1





"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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

– Dai
4 hours ago





"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" - I feel it's common in WinForms to re-use event-handlers for multiple controls and events to reduce boilerplate when the handler just triggers some common action not specific to the control or event that was invoked.

– Dai
4 hours ago




4




4





@Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

– Eric Lippert
3 hours ago





@Dai: Sure, and to be clear, I am not suggesting that there is any problem with variant conversions from method group to delegate. That's pretty common in webforms scenarios and that is well-tested. It's when variance on the delegate type itself is in play that delegate combination had bugs. If I have some free time I'll see if I can recreate my test cases from 12 years ago and see if they still repro or not.

– Eric Lippert
3 hours ago













There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

– Aaron M. Eshbach
21 mins ago







There is an open issue on the F# GitHub for adding variance support to the language, which explicitly mentions events as a possible enhancement. If anyone is working on this feature, they may know what the current status of the CLR bugs would be. github.com/fsharp/fslang-suggestions/issues/162

– Aaron M. Eshbach
21 mins ago






















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54907236%2fwhy-wasnt-teventargs-made-contravariant-in-the-standard-event-pattern-in-the-n%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

“%fieldName is a required field.”, in Magento2 REST API Call for GET Method Type The Next...

How to change City field to a dropdown in Checkout step Magento 2Magento 2 : How to change UI field(s)...

夢乃愛華...