List, array and IEnumerable covariance

萝らか妹 提交于 2019-12-23 09:45:57

问题


I'll start with several postulates to better explain the context of my question:

Array Covariance

Postulate 1.1

An array of a value type is not covariant. int[] cannot pass for object[].

Postulate 1.2

An array of a reference type is covariant with a valid IEnumerable. string[] can pass for IEnumerable<object>).

Postulate 1.3

An array of a reference type is covariant with a valid covariant array. string[] can pass for object[].

List Covariance

Postulate 2.1 (same as 1.1)

A list of a value type is not covariant. List<int> cannot pass for List<object>.

Postulate 2.2 (same as 1.2)

A list of a reference type is covariant with a valid IEnumerable. List<string> can pass for IEnumerable<object>).

Postulate 2.3 (different from 1.3)

A list of a reference type is not covariant with a valid covariant List. List<string> cannot pass for List<object>).


My question concerns postulates 1.3, 2.2 and 2.3. Specifically:

  1. Why can string[] pass for object[], but List<string> not for List<object>?
  2. Why can List<string> pass for IEnumerable<object> but not for List<object>?

回答1:


List covariance is unsafe:

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

Array covariance is also unsafe for the same reason:

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

array covariance in C# is recognised as a mistake, and has been present since version 1.

Since the collection cannot be modified through the IEnumerable<T> interface, it is safe to type a List<string> as an IEnumerable<object>.




回答2:


Arrays are covariant, but a System.Int32[] does not hold references to things which are derived from System.Object. Within the .NET runtime, each value-type definition actually defines two kinds of things: a heap object type and a value (storage location) type. The heap object type is derived from System.Object; the storage location type is implicitly convertible to the heap object type (which in turn derives from System.Object) but does not itself actually derive from System.Object nor anything else. Although all arrays, including System.Int32[] are heap-object types, the individual elements of a System.Int32[] are instances of the storage location type.

The reason that a String[] can be passed to code expecting an Object[] is that the former contains "references to heap-object instances of type derived from type String", and the latter likewise for type Object. Since String derives from Object, a reference to a heap-object of a type derived from String will also be a reference to a heap object which derives from Object, and a String[] will contain references to heap objects which derive from Object--exactly what code would expect to read from an Object[]. By contrast, because an int[] [i.e. System.Int32[]] does not contain references to heap-object instances of type Int32, its contents will not conform to the expectations of code which is expecting Object[].



来源:https://stackoverflow.com/questions/17061936/list-array-and-ienumerable-covariance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!