问题
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:
- Why can
string[]pass forobject[], butList<string>not forList<object>? - Why can
List<string>pass forIEnumerable<object>but not forList<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