看下面的示例(部分取自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>(); }
这是一个协方差问题吗?将来的C#版本中是否会支持此方法,并且有任何巧妙的解决方法(仅使用.NET 2.0)吗?
好吧,这当然在C#4中将不受支持。存在一个基本问题:
List<Giraffe> giraffes = new List<Giraffe>(); giraffes.Add(new Giraffe()); List<Animal> animals = giraffes; animals.Add(new Lion()); // Aargh!
保持长颈鹿的安全:对不安全的差异说不。
数组版本之所以起作用,是因为数组 确实 支持引用类型差异以及执行时间检查。泛型的重点是提供 编译时 类型的安全性。
在C#4中,将支持 安全的 通用变量,但仅支持接口和委托。这样您就可以执行以下操作:
Func<string> stringFactory = () => "always return this string"; Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4
Func<out T>是 协变的 ,T因为T仅用于输出位置。比较与之Action<in T>相反的T原因,因为T它仅在此处的输入位置使用,因此很安全:
Func<out T>
T
Action<in T>
Action<object> objectAction = x => Console.WriteLine(x.GetHashCode()); Action<string> stringAction = objectAction; // Safe, allowed in C# 4
IEnumerable<out T> 也是协变的,这在C#4中是正确的,正如其他人指出的那样:
IEnumerable<out T>
IEnumerable<Animal> animals = new List<Giraffe>(); // Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.
关于在C#2中解决此问题的方式,您是否需要维护 一个 列表,还是愿意创建一个新列表?如果可以接受,List<T>.ConvertAll是您的朋友。
List<T>.ConvertAll