C# variance problem: Assigning List as List

Well this certainly won’t be supported in C# 4. There’s a fundamental problem:

List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = giraffes;
animals.Add(new Lion()); // Aargh!

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<string> stringFactory = () => "always return this string";
Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4

Func<out T> is covariant in T because T is only used in an output position. Compare that with Action<in T> which is contravariant in T because T is only used in an input position there, making this safe:

Action<object> objectAction = x => Console.WriteLine(x.GetHashCode());
Action<string> stringAction = objectAction; // Safe, allowed in C# 4

IEnumerable<out T> is covariant as well, making this correct in C# 4, as pointed out by others:

IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.

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.

Leave a Comment