8

我只是在 .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
}
4

3 回答 3

11

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

该转换有效,因为类型参数 ofIEnumerable<T>现在是协变的(这就是outinout T所做的)。这意味着您可以自由地将 anIEnumerable<Derived>转换为 a 。IEnumerable<Base>

第一行,这似乎是相同的情况,不起作用,因为int它是一个值类型。接口变体根本不适用于值类型,因为值类型并不真正继承自System.Object; 它们可以装箱object,但不一样。该文档提到

差异仅适用于引用类型;如果为变体类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

最后,第三行不起作用,因为 的类型参数List<T>invariant。你可以看到out它的类型参数上没有;规则不允许这样做,因为List<T>它不是接口:

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

于 2012-09-11T18:00:16.863 回答
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/

于 2012-09-11T17:59:37.340 回答
0

从 派生的每个类型的定义,System.ValueType除了 之外System.Enum,实际上定义了两种东西:堆对象类型和存储位置类型。后者的实例可以隐式转换为前者(复制其中包含的数据),而前者的实例可以显式类型转换为后者(同样);尽管这两种事物都由相同的 描述System.Type,并且尽管它们具有相同的成员,但它们的行为却截然不同。

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

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

于 2013-01-21T18:41:28.007 回答