Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Variance in C# 4.0

Generic Variance in C# 4.0 has been implemented in such a way that it's possible to write the following without an exception (which is what would happen in C# 3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

[Example non-functional: See Jon Skeet's answer]

I recently attended a conference where Jon Skeet gave an excellent overview of Generic Variance, but I'm not sure I'm completely getting it - I understand the significance of the in and out key words when it comes to contra and co-variance, but I'm curious to what happens behind the scenes.

What does the CLR see when this code is executed? Is it implicitly converting the List<int> to List<object> or is it simply built in that we can now convert between derived types to parent types?

Out of interest, why wasn't this introduced in previous versions and what's the main benefit - ie real world usage?

More info on this post for Generic Variance (but question is extremely outdated, looking for real, up-to-date information)

like image 874
Daniel May Avatar asked Feb 05 '10 14:02

Daniel May


1 Answers

No, your example wouldn't work for three reasons:

  • Classes (such as List<T>) are invariant; only delegates and interfaces are variant
  • For variance to work, the interface has to only use the type parameter in one direction (in for contravariance, out for covariance)
  • Value types aren't supported as type arguments for variance - so there's no converstion from IEnumerable<int> to IEnumerable<object> for example

(The code fails to compile in both C# 3.0 and 4.0 - there's no exception.)

So this would work:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

The CLR just uses the reference, unchanged - no new objects are created. So if you called objects.GetType() you'd still get List<string>.

I believe it wasn't introduced earlier because the language designers still had to work out the details of how to expose it - it's been in the CLR since v2.

The benefits are the same as other times where you want to be able to use one type as another. To use the same example I used last Saturday, if you've got something implements IComparer<Shape> to compare shapes by area, it's crazy that you can't use that to sort a List<Circle> - if it can compare any two shapes, it can certainly compare any two circles. As of C# 4, there'd be a contravariant conversion from IComparer<Shape> to IComparer<Circle> so you could call circles.Sort(areaComparer).

like image 86
Jon Skeet Avatar answered Oct 05 '22 04:10

Jon Skeet