C# 差异问题:Assigning List<Derived>作为列表<Base> [英] C# variance problem: Assigning List<Derived> as List<Base>
问题描述
看下面的例子(部分摘自MSDN 博客):
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>();
}
这是一个协方差问题吗?将来的 C# 版本会支持这点吗?是否有任何巧妙的解决方法(仅使用 .NET 2.0)?
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)?
推荐答案
C# 4 肯定不支持这个.有一个基本问题:
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.
在 C# 4 中,将支持安全泛型变化,但仅适用于接口和委托.所以你将能够做到:
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
在 T
中是 covariant 因为 T
仅用于输出位置.将其与 T
中的逆变 Action
进行比较,因为 T
仅用于那里的输入位置,使其安全:
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
也是协变的,正如其他人指出的那样,这在 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.
就您在 C# 2 中的情况解决此问题而言,您是否需要维护一个列表,或者您是否愿意创建一个新列表?如果这是可以接受的,List
就是你的朋友.
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.
这篇关于C# 差异问题:Assigning List<Derived>作为列表<Base>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!