Look at the following example (partially taken from MSDN Blog):
class Animal { }
class Giraffe : Animal { }
static void Main(string[] args)
{
// Array assignment works, but...
Animal[] animals = new Giraffe[10];
// implicit...
List<Animal> animalsList = new List<Giraffe>();
// ...and explicit casting fails
List<Animal> animalsList2 = (List<Animal>) new List<Giraffe>();
}
Is this a covariance problem? Will this be supported in the future C# release and are there any clever workarounds (using only .NET 2.0)?
Well this certainly won't be supported in C# 4. There's a fundamental problem:
Keep giraffes safe: just say no to unsafe variance.
The array version works because arrays do support reference type variance, with execution time checking. The point of generics is to provide compile-time type safety.
In C# 4 there will be support for safe generic variance, but only for interfaces and delegates. So you'll be able to do:
Func<out T>
is covariant inT
becauseT
is only used in an output position. Compare that withAction<in T>
which is contravariant inT
becauseT
is only used in an input position there, making this safe:IEnumerable<out T>
is covariant as well, making this correct in C# 4, as pointed out by others:In terms of working around this in your situation in C# 2, do you need to maintain one list, or would you be happy creating a new list? If that's acceptable,
List<T>.ConvertAll
is your friend.In terms of
List<T>
, I'm afraid you're out of luck. However, .NET 4.0/C# 4.0 adds support for covariant/contravariant interfaces. Specifically,IEnumerable<T>
is now defined asIEnumerable<out T>
, which means that the type parameter is now covariant.This means you can do something like this in C# 4.0...
Note: Array types have also been covariant (at least since .NET 1.1).
I think it's a shame that variance support wasn't added for
IList<T>
and other similar generic interfaces (or generic classes even), but oh well, at least we have something.It will work in C#4 for
IEnumerable<T>
, so you can do:However
List<T>
is not a covarient projection, so you cannot assign lists as you have done above since you could do this:Which is clearly not valid.
Covariance/contravariance can't be supported on mutable collections as others have mentioned because it's impossible to guarantee type safety both ways at compile time; however, it is possible to do a quick one-way conversion in C# 3.5, if that is what you're looking for:
Of course it's not the same thing, it's not actually covariance - you're actually creating another list, but it is a "workaround" so to speak.
In .NET 2.0, you can take advantage of array covariance to simplify the code:
But be aware that you're actually creating two new collections here.