C#差异问题:分配列表<;派生>;as列表<;基地>;

C#差异问题:分配列表<;派生>;as列表<;基地>;,c#,covariance,C#,Covariance,请看以下示例(部分摘自): 类动物{} 长颈鹿类:动物{} 静态void Main(字符串[]参数) { //数组赋值有效,但是。。。 动物[]动物=新长颈鹿[10]; //含蓄的。。。 List animalsList=新列表(); //…显式强制转换失败 List animallist2=(List)new List(); } 这是协方差问题吗?这将在未来的C#发行版中得到支持吗?是否有任何巧妙的解决方法(仅使用.NET 2.0)?好的,这在C#4中肯定不受支持。有一个根本问题: List

请看以下示例(部分摘自):

类动物{}
长颈鹿类:动物{}
静态void Main(字符串[]参数)
{
//数组赋值有效,但是。。。
动物[]动物=新长颈鹿[10];
//含蓄的。。。
List animalsList=新列表();
//…显式强制转换失败
List animallist2=(List)new List();
}

这是协方差问题吗?这将在未来的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!
IEnumerable
也是协变的,这在C#4中是正确的,正如其他人所指出的:

IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.
IEnumerable动物=新列表();
//无法向动物添加Lion,因为'IEnumerable'是只读接口。
在你C#2的情况下,为了解决这个问题,你需要维护一个列表,还是愿意创建一个新列表?如果可以接受,
List.ConvertAll
是您的朋友。

它将在C#4中为
IEnumerable
工作,因此您可以执行以下操作:

IEnumerable<Animal> animals = new List<Giraffe>();
IEnumerable动物=新列表();
但是,
List
不是共变投影,因此您不能像上面那样分配列表,因为您可以这样做:

List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());
列出动物=新建列表();
添加(新猴子());
这显然是无效的。

关于
列表,恐怕你运气不好。然而,.NET 4.0/C#4.0增加了对协变/逆变接口的支持。具体来说,
IEnumerable
现在定义为,这意味着类型参数现在是协变的

这意味着您可以在C#4.0中执行类似的操作

//隐式转换
IEnumerable animalsList=新列表();
//显式铸造
IEnumerable animallist2=(IEnumerable)新列表();
注意:数组类型也是协变的(至少从.NET1.1开始)


我觉得很遗憾没有为
IList
和其他类似的泛型接口(甚至泛型类)添加差异支持,但是哦,至少我们有一些东西。

协变/逆变不能像其他人提到的那样在可变集合上得到支持,因为不可能在编译时保证两种方式的类型安全;但是,如果您希望在C#3.5中进行快速单向转换,也可以这样做:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes.Cast<Animal>().ToList();
List giraffes=newlist();
列出动物=长颈鹿.Cast().ToList();
当然,这不是同一件事,它实际上不是协方差——你实际上在创建另一个列表,但可以说这是一个“变通方法”

在.NET 2.0中,可以利用数组协方差简化代码:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = new List<Animal>(giraffes.ToArray());
List giraffes=newlist();
列出动物=新列表(giraffes.ToArray());
但是请注意,您实际上在这里创建了两个新集合。

GenericClass
GenericClass
以及
GenericClass
的两个不同的封闭构造泛型类型打开泛型类型,它们不会从另一个继承一个

因此,您不能将
GenericClass
强制转换为
GenericClass


对于我的简单应用程序,我认为这些都是很好的解决方法。至少它们在可读性方面是好的,而不是在性能方面。@JohnAskew:我不确定你的观点是什么-在
IList
上没有
Cast
方法,它是
Enumerable.Cast
扩展方法,它接受
IEnumerable
并将每个元素显式地强制转换为
T2
,然后返回它(作为
IEnumerable
,而不是
IList
)。它与
IList
完全无关,只是
IList
恰好继承自
IEnumerable
,因此支持
Enumerable
扩展方法。这里没有任何协方差。我没有看到对C#3.5的引用,而是指的是C#4.0,其中IEnumerable接口s更新为IEnumerable,Cast方法更改为使用协方差:IEnumerable typedSource=source作为IEnumerable,而不是使用显式强制转换的迭代。您无法使用声明端方差批注安全地使IList协方差。
动物有什么问题。添加(new Lion())//Aargh!
?如果你可以铸造一头
狮子,并将其用作
动物
,如果你将
动物
中的所有元素都用作
动物
,那么问题出在哪里?@Jeff:因为
动物
实际上指的是
列表
。所以你最终会在
里得到
狮子
的引用st
,这是不好的……值得一提的是,更适合列表的可能是IReadOnlyList,它也是安全协变的,是不可变的。@x0n:我很高兴你提到这一点,但我不会用后面介绍的类型更新我的所有旧答案。@AlexandreDaubricourt:但其他一些代码可能引用与aList
(如我的示例中所示)。因此,当您尝试执行任何操作时,都会出现错误,但这可能要晚很多时间。最好尽早发现问题,IMO。
List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());
// implicit casting
IEnumerable<Animal> animalsList = new List<Giraffe>();

// explicit casting
IEnumerable<Animal> animalsList2 = (IEnumerable<Animal>) new List<Giraffe>();
List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes.Cast<Animal>().ToList();
List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = new List<Animal>(giraffes.ToArray());
class A2 : A1;
class B2 : B1;

var a2 = new A2();
var b2 = new B2();
var x = (A1)b2;
var x = (GenericClass<>)b;
List<> list;