Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Animals[] animals = new Cat[5] compiles, but List<Animal> animals = new List<Cat>() does not?

In his book C# in Depth, Jon Skeet tries to answer the following question:

Why can't I convert List<string> to List<object>?

To explain it, he started with a code-snippet, which includes these two lines:

Animal[] animals = new Cat[5]; //Ok. Compiles fine!            
List<Animal> animals = new List<Cat>(); //Compilation error!

As the comments read, the first one compiles fine, but the second one gives compilation error. I didn't really understand the reason. Jon Skeet explained this with saying only that first one compiles because in .NET, arrays are covariant, and second one doesn't compile because generics are not covariant (they're invariant, instead). And furthermore, arrays are covariant in .NET, because arrays are covariant in Java and .NET made it similar to Java.

I'm not completely statisfied with this short-answer. I want to know it in a more detail, with some insight into how compiler handles the difference, and how it generates IL and all.

Also, if I write (taken from the book itself):

Animal[] animals = new Cat[5]; //Ok. Compiles fine!
animals.Add(new Turtle()); //this too compiles fine!

It compiles fine, but fails at runtime. If it has to fail at runtime(which means what I write shouldn't make sense), then why does it compile in the first place? Can I use the instance animals in my code, and which also runs with no runtime error?

like image 225
Nawaz Avatar asked Sep 28 '11 19:09

Nawaz


1 Answers

Arrays have a weird history with variance in .NET. Proper support for variance was added in the 2.0 version of the CLR - and the language in C# 4.0. Arrays however, have always had a covariant behavior.

Eric Lippert goes into great detail on that in a blog post.

The interesting bit:

Ever since C# 1.0, arrays where the element type is a reference type are covariant. This is perfectly legal:

Animal[] animals = new Giraffe[10];

Since Giraffe is smaller than Animal, and “make an array of” is a covariant operation on types, Giraffe[] is smaller than Animal[], so an instance fits into that variable.

Unfortunately, this particular kind of covariance is broken. It was added to the CLR because Java requires it and the CLR designers wanted to be able to support Java-like languages. We then up and added it to C# because it was in the CLR. This decision was quite controversial at the time and I am not very happy about it, but there’s nothing we can do about it now.

Emphasis added by myself.

like image 85
vcsjones Avatar answered Sep 28 '22 01:09

vcsjones