I've been hearing/reading a lot about covariance issues in C# and I wanted to pose a few questions & scenarios so hopefully I can clear up my confusion on the matter.
Throughout these examples, please assume the following is always defined:
public class Apple : Fruit {}
My first example:
IList<Apple> apples = GetApples();
IList<Fruit> fruits = apples;
This should work, correct? I tested this a couple of times in C# and it compiled fine & ran fine (my tests for my first example were slightly more than this, as I had polymorphic calls that printed stuff to the console).
Second example:
IList<Apple> apples = GetApples();
IList<object> fruits = apples;
In this second example, my understanding is that this should not compile and is the root of the covariance issues being resolved in .NET 4.0. Please correct me if I am wrong. I also am aware that .NET 4.0 does not allow covariance/contravariance between concrete types, only interfaces.
Finally, I'd like to get some definitions. I'm not quite clear of the meaning behind these 3 terms:
As for the last word, I used it a lot in C++ to refer to changes that have rules implied to them. For example, if I have an integer and it is only allowed to have a value between 1 and 10, the "invariance" is that it can only be between 1 and 10. I might be misunderstanding this and I'm also not sure if this definition translates well to C# for this specific discussion.
My goal is to understand exactly what the covariance or casting issues are with generic interfaces in C#. The examples I posted are my understanding of where the problem lies. If all examples compile/function fine, please present an example that does reproduce the most common covariance/contravariance/casting issues in C#. I need to know this so I can identify and explain the problem to others.
The IList<T>
interface is not defined to be covariant because it supports an Add
method which mutates the object.
Consider the following:
IList<Apple> apples = GetApples();
IList<object> fruits = apples;
fruits.Add(new Banana());
You could now get a Banana
from apples
, which is certainly not what was intended. Therefore, the IList
interface does not support covariance (and it never will), and should cause a compile error.
You should have the same problem with
IList<Apple> apples = GetApples();
IList<Fruit> fruits = apples;
fruits.Add(new Banana());
so I'm not sure why it compiled for you.
The IEnumerable<out T>
interface can be covariant (and it is in .NET 4.0 and later), because IEnumerable
only supports reading elements from a collection.
The Scala language has a similar concept of covariant and contravariant objects, and the chapter from Programming in Scala that discusses generics should serve as a good introduction to covariance in C# as well.