C# 方差问题:分配 List作为列表
查看以下示例(部分取自 MSDN 博客):
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)?
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)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
嗯,这肯定不会在 C# 4 中得到支持。有一个根本问题:
保证长颈鹿的安全:对不安全的方差说“不”。
数组版本之所以有效,是因为数组确实支持引用类型差异,并具有运行时检查。泛型的要点是提供编译时类型安全性。
在 C# 4 中,将支持安全泛型变体,但仅适用于接口和委托。因此,您将能够执行以下操作:
Func
is covariant inT
因为T
仅用于输出位置。将其与Action
进行比较,后者在T
中是逆变的,因为T
仅在输入位置中使用,从而使其安全:< code>IEnumerable也是协变的,这在 C# 4 中是正确的,正如其他人指出的:
在 C# 2 中解决这个问题时,您是否需要维护一个列表,或者您愿意创建一个新列表吗?如果可以接受,
List.ConvertAll
就是您的朋友。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 runtime 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.它将在 C#4 中为
IEnumerable
工作,因此您可以执行以下操作:但是
List
不是协变投影,因此您无法按照您的方式分配列表已经完成了上面的操作,因为您可以这样做:这显然是无效的。
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.
就
List
而言,恐怕您不走运。但是,.NET 4.0/C# 4.0 添加了对协变/逆变接口的支持。具体来说,IEnumerable
现在定义为IEnumerable
,这意味着类型参数现在协变。这意味着您可以在 C# 4.0 中执行类似的操作...
注意:数组类型也是协变的(至少自 .NET 1.1 以来)。
我认为遗憾的是没有为
IList
和其他类似的通用接口(甚至是通用类)添加方差支持,但是哦,好吧,至少我们有一些东西。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.正如其他人提到的那样,可变集合不支持协变/逆变,因为不可能在编译时保证两种方式的类型安全;但是,如果您正在寻找的话,可以在 C# 3.5 中进行快速单向转换:
当然,这不是同一件事,它实际上不是协方差 - 您实际上是在创建另一个列表,但它可以说是一种“解决方法”。
在 .NET 2.0 中,您可以利用数组协变来简化代码:
但请注意,您实际上是在此处创建两个新集合。
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.