我将从几个假设开始,以更好地解释我的问题的背景:
值类型的数组不是协变的。 int[]
无法通过object[]
。
引用类型的数组与有效的IEnumerable
协变。 string[]
可以传递IEnumerable<object>
。
引用类型的数组与有效的协变数组协变。 string[]
可以传递object[]
。
值类型的列表不是协变的。 List<int>
无法通过List<object>
。
引用类型列表与有效IEnumerable
协变。 List<string>
可以传递IEnumerable<object>
。
引用类型列表与有效协变List
不一致。 List<string>
无法传递List<object>
。
我的问题涉及假设1.3,2.2和2.3。具体做法是:
string[]
object[]
可以通过,List<string>
为List<object>
而不是List<string>
?IEnumerable<object>
可以通过List<object>
而不能通过{{1}}?答案 0 :(得分: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#中的数组协方差被识别为as a mistake,并且自版本1以来一直存在。
由于无法通过IEnumerable<T>
界面修改集合,因此可以安全地将List<string>
键入IEnumerable<object>
。
答案 1 :(得分:-1)
数组是协变的,但是System.Int32[]
不包含对从System.Object
派生的内容的引用。在.NET运行时中,每个值类型定义实际上定义了两种类型:堆对象类型和值(存储位置)类型。堆对象类型派生自System.Object
;存储位置类型可以隐式转换为堆对象类型(后者又来自System.Object
)但实际上并不是从System.Object
派生而是从其他任何东西派生。虽然所有数组(包括System.Int32[]
)都是堆对象类型,但System.Int32[]
的各个元素是存储位置类型的实例。
String[]
可以传递给期望Object[]
的代码的原因是前者包含“对从类型String
派生的类型的堆对象实例的引用”,以及后者同样适用于Object
型。由于String
派生自Object
,因此对从String
派生的类型的堆对象的引用也将是对从Object
派生的堆对象的引用,并且String[]
将包含对派生自Object
的堆对象的引用 - 确切地说是期望从Object[]
读取的代码。相比之下,因为int[]
[即System.Int32[]
]不包含对Int32
类型的堆对象实例的引用,其内容将不符合期望Object[]
的代码期望。