C#中通用列表的类型转换

时间:2017-01-23 12:46:55

标签: c# generics

我试图弄清楚为什么在第一种情况下不可能进行类型转换,但在第二种情况下可以实现。请看下面的代码:

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

4 个答案:

答案 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 ConversionsGeneric 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的说明,请参阅以下内容。认为它会澄清你所遇到的问题:

http://tomasp.net/blog/variance-explained.aspx/