我试图弄清楚为什么在第一种情况下不可能进行类型转换,但在第二种情况下可以实现。请看下面的代码:
var strList = new List<string>{"One", "Two", "Three"};
List<object> objList = (List<object>) strList; // <<<< why is not converted? - Case 1
IEnumerable<object> ienumList = strList; // <<<< why is converted? - Case 2
答案 0 :(得分:9)
虽然comment referring to covariance and contravariance对基本原则提出了一个很好的观点,但我将回答所提出案例中的具体影响:
List<object> objList = (List<object>) strList;
objList
是可写的。它允许你这样做:
objList.Add(new Object());
但是,objList
仍然是与strList
相同的实例。您刚刚将System.Object
的实例添加到List<string>
!
编译器不允许这种情况发生。因此,List<string>
无法转换为List<object>
。
在第二种情况下,另一方面,您检索一个可枚举:
IEnumerable<object> ienumList = strList;
无法修改生成的可枚举,因为IEnumerable<T>
不提供任何改变实例的成员。因此,类型参数T
可以(并且)标记为out
关键字,允许显示的类型转换。
答案 1 :(得分:2)
这里有两个主要概念:Type Conversions和Generic Interfaces Variance。方差是主导方法。
案例1:在List<T>
类定义中,我们没有泛型参数的差异。因此,我们在List<object>
和List<string>
之间没有定义任何关系。它们是不变的。因此,隐式和显式类型转换都是不可能的。
案例2: List<T>
实现了IEnumerable<out T>
,它是协变泛型类型,因此List<string>
可以隐式强制转换为IEnumerable<object>
详情:
为什么List<T>
不允许差异,IEnumerable<T>
允许差异?
泛型的关键是提供编译时类型的安全性。
因为List<T>
是可写的,如果没有编译时间检查,我们可以编写以下内容并产生运行时错误:
List<string> stringList = new List<string>();
stringList.Add("some string"); // we are safe
List<object> objectList = stringList;
objectList.Add((new Object()); // Aargh!
// we are trying to put an object to a list of strings!
因此List<T>
没有不安全的差异。
但IEnumerable<out T>
是只读的。它没有提供修改引用实例的方法。
IEnumerable<object> objectList = new List<string>();
// we can't add a string to the objectList,
// as `IEnumerable<out T>` is a read-only interface.
因此可能存在安全差异。
答案 2 :(得分:0)
如果您使用的是框架3.5或更高版本,则可以执行以下操作:
var strList = new List<string>{"One", "Two", "Three"};
List<object> objList = strList.Cast<object>().ToList();
你可以看看@Hagashen Naidu它对这个案子有一个非常好的解释。
答案 3 :(得分:-1)
有关co和contra-variance的说明,请参阅以下内容。认为它会澄清你所遇到的问题: