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?
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