泛型铸造

时间:2012-09-11 17:55:16

标签: c# .net generics casting

我只是在.NET泛型类型转换中没有得到什么。 有人可以解释下面的代码片段中会发生什么吗?

void Main()
{
    IEnumerable<int> ints = new List<int>();
    IEnumerable<string> strings = new List<string>();

    var rez1=(IEnumerable<object>)ints; //runtime error
    var rez2=(IEnumerable<object>)strings; //works
    var rez3=(List<object>)strings; //runtime error
}

3 个答案:

答案 0 :(得分:11)

让我们从第二行开始,这是最简单的。

该投射有效,因为IEnumerable<T>的类型参数现在为covariant(这就是out中的out T所做的事情)。这意味着您可以自由地将IEnumerable<Derived>投射到IEnumerable<Base>

第一行似乎是相同的情况,因为int是值类型,所以不起作用。界面差异根本不适用于值类型,因为值类型并不真正从System.Object继承;它们可以装箱object,但这不一样。文档提到了

  

差异仅适用于参考类型;如果指定值类型   对于变量类型参数,该类型参数对于该变量是不变的   结果构造类型。

最后,第三行不起作用,因为List<T>的类型参数是不变。你可以看到它的类型参数没有out;规则不允许因为List<T>不是界面:

  

在.NET Framework 4中,变体类型参数仅限于   通用接口和通用委托类型。

答案 1 :(得分:2)

这是因为界面协方差仅适用于引用类型。当然,Int32是一种值类型。

这提供了更多信息:http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx

这样做:http://ericlippert.com/2011/09/19/inheritance-and-representation/

答案 2 :(得分:0)

除了System.ValueType之外,从System.Enum派生的每个类型的定义实际上定义了两种类型:堆对象类型和存储位置类型。后者的实例可以隐式转换为前者(制作其中包含的数据的副本),前者的实例可以明确地对后者进行类型转换(同样);即使两种事物都由同一System.Type描述,虽然它们具有相同的成员,但它们的行为却截然不同。

List<AnyClassType>期望拥有一堆堆对象引用;有问题的列表是List<String>List<StringBuilder>List<Button>还是其他什么,可能是列表用户感兴趣的,但{{1}并不感兴趣本身。如果将List<T>转换为List<Button>,则调用其IEnumerable<Control>方法的人将希望获得一个对象,该对象将输出对从GetEnumerator()派生的堆对象的引用; Control的回报将满足这种期望。相比之下,如果有人要将List<Button>.GetEnumerator()强制转换为List<Int32>,那么调用List<Object>的人会期望输出堆对象引用的东西,但是GetEnumerator()会产生一些东西输出值类型整数。

可以将List<Integer>.GetEnumerator值存储到Int32List<Object>;将整数存储到这样的列表中会将其转换为堆对象形式并存储对它的引用;调用List<ValueType>会产生输出堆引用的东西。但是,无法指定此类列表仅包含与GetEnumerator()对应的堆类型的实例。在C ++ / CLI中,可以声明“对堆存储的值类型的引用”类型的变量,但.net中泛型类型背后的机制不能用于这些类型。