6

我将从几个假设开始,以更好地解释我的问题的背景:

数组协方差

假设 1.1

值类型的数组不是协变的。int[]不能通过object[]

假设 1.2

引用类型的数组与有效的IEnumerable. string[]可以通过IEnumerable<object>)。

假设 1.3

引用类型的数组与有效的协变数组是协变的。string[]可以通过object[]

列出协方差

假设 2.1(与 1.1 相同)

值类型的列表不是协变的。List<int>不能通过List<object>

假设 2.2(与 1.2 相同)

引用类型的列表与有效的IEnumerable. List<string>可以通过IEnumerable<object>)。

假设 2.3(不同于 1.3)

引用类型的列表与有效的协变不协变ListList<string>不能通过List<object>)。


我的问题涉及假设 1.3、2.2 和 2.3。具体来说:

  1. 为什么可以string[]传为object[],而List<string>不能为List<object>
  2. 为什么可以List<string>通过 forIEnumerable<object>但不能 for List<object>
4

2 回答 2

13

列表协方差不安全:

List<string> strings = new List<string> { "a", "b", "c" };
List<object> objects = strings;
objects.Add(1);              //

出于同样的原因,数组协方差也是不安全的:

string[] strings = new[] { "a", "b", "c" };
object[] objects = strings;
objects[0] = 1;              //throws ArrayTypeMismatchException

C# 中的数组协方差被认为是一个错误,并且从版本 1 开始就存在。

由于无法通过IEnumerable<T>接口修改集合,因此将 a 键入List<string>为 是安全的IEnumerable<object>

于 2013-06-12T09:29:17.297 回答
-1

数组是协变的,但 aSystem.Int32[]不包含对派生自 的事物的引用System.Object。在 .NET 运行时中,每个值类型定义实际上定义了两种事物:堆对象类型和值(存储位置)类型。堆对象类型派生自System.Object; 存储位置类型可以隐式转换为堆对象类型(它又派生自System.Object),但它本身实际上并没有派生自System.Object其他任何东西。尽管所有数组,包括都是堆对象类型,但aSystem.Int32[]的各个元素System.Int32[]都是存储位置类型的实例。

String[]可以将 a 传递给期望 an 的代码的原因Object[]是前者包含“对派生自 type 的类型的堆对象实例的引用String”,而后者同样适用于 type Object。由于String派生自Object,对派生于的类型String的堆对象的引用也将是对派生自的堆对象的引用Object,并且 aString[]将包含对派生自的堆对象的引用——Object确切地说是期望从中读取的代码一个Object[]。相比之下,因为int[][ie System.Int32[]] 不包含对类型的堆对象实例的引用Int32,所以它的内容将不符合期望的代码的期望Object[]

于 2013-06-12T17:35:22.127 回答