简体   繁体   English

泛型铸造

[英]Generics type casting

I just don't get something in the .NET generic type casting. 我只是在.NET泛型类型转换中没有得到什么。 Can someone explain what happens in the following code snippet? 有人可以解释下面的代码片段中会发生什么吗?

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
}

Let's start with the second line which is easiest. 让我们从第二行开始,这是最简单的。

That cast works because the type parameter of IEnumerable<T> is now covariant (that's what the out in out T does). 这铸作品,因为类型参数IEnumerable<T>现在是协变 (这是什么outout T一样)。 This means you can cast an IEnumerable<Derived> to an IEnumerable<Base> freely. 这意味着您可以自由地将IEnumerable<Derived>转换为IEnumerable<Base>

The first line, which would seem to be the same case, does not work because int is a value type. 第一行似乎是相同的情况,因为int是值类型,所以不起作用。 Interface variance does not work with value types at all because value types do not really inherit from System.Object ; 接口方差根本不适用于值类型,因为值类型并不真正从System.Object继承; they can be boxed into an object , but that's not the same. 它们可以被装箱object ,但是这是不一样的。 The documentation mentions that 文件提到了这一点

Variance applies only to reference types; 差异仅适用于参考类型; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type. 如果为变量类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

Finally, the third line does not work because the type parameter of List<T> is invariant . 最后,第三行不起作用,因为List<T>的类型参数是不变的 You can see there is no out on its type parameter; 你可以看到它的类型参数没有out ; the rules disallow that because List<T> is not an interface: 规则不允许因为List<T>不是接口:

In the .NET Framework 4, variant type parameters are restricted to generic interface and generic delegate types. 在.NET Framework 4中,变体类型参数仅限于通用接口和通用委托类型。

This is because interface covariance only works with reference types. 这是因为界面协方差仅适用于引用类型。 Int32, of course, is a value type. 当然,Int32是一种值类型。

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

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

The definition of every type which derives from System.ValueType , with the exception of System.Enum , actually defines two kinds of things: a heap object type, and a storage-location type. System.ValueType派生的每个类型的定义( System.Enum除外)实际上定义了两种类型:堆对象类型和存储位置类型。 Instances of the latter may be implicitly converted to the former (making a copy of the data contained therein), and instances of the former may be explicitly typecast to the latter (likewise); 后者的实例可以隐式转换为前者(制作其中包含的数据的副本),前者的实例可以明确地对后者进行类型转换(同样); even though both kinds of things are described by the same System.Type , and although they have the same members, they behave very differently. 即使两种事物都由相同的System.Type描述,虽然它们具有相同的成员,但它们的行为却截然不同。

A List<AnyClassType> will expect to hold a bunch of heap-object references; List<AnyClassType>将包含一堆堆对象引用; whether the list in question is a List<String> , List<StringBuilder> , List<Button> , or whatever, may be of interest to users of the list, but isn't really of interest to the List<T> itself. 有问题的列表是List<String>List<StringBuilder>List<Button>还是其他,列表的用户可能会感兴趣,但List<T>本身并不感兴趣。 If one casts a List<Button> to an IEnumerable<Control> , someone who calls its GetEnumerator() method will expect to get an object which will output references to heap objects that derive from Control ; 如果将List<Button>转换为IEnumerable<Control> ,则调用其GetEnumerator()方法的人将期望获得一个对象,该对象将输出对从Control派生的堆对象的引用; the return from List<Button>.GetEnumerator() will satisfy that expectation. List<Button>.GetEnumerator()的返回将满足该期望。 By contrast, if someone were to cast a List<Int32> to List<Object> , someone who called GetEnumerator() would expect something that would output heap object references, but List<Integer>.GetEnumerator will instead yield something that outputs value-type integers. 相比之下,如果有人将List<Int32>List<Object> ,那么调用GetEnumerator()会期望输出堆对象引用的东西,但List<Integer>.GetEnumerator会产生输出值的东西 -类型整数。

It's possible to store Int32 values into a List<Object> or a List<ValueType> ; 可以将Int32值存储到List<Object>List<ValueType> ; storing an integer to such a list will convert it to its heap object form and store a reference to it; 将整数存储到这样的列表中会将其转换为堆对象形式并存储对它的引用; calling GetEnumerator() would yield something that outputs heap references. 调用GetEnumerator()会产生输出堆引用的东西。 There is no way to specify, however, that such a list will only contain instances of the heap type corresponding to Int32 . 但是,无法指定此类列表仅包含与Int32对应的堆类型的实例。 In C++/CLI, it's possible to declare variables of type "reference to heap-stored valuetype", but the mechanisms behind generic types in .net cannot work with such types. 在C ++ / CLI中,可以声明“对堆存储的值类型的引用”类型的变量,但.net中泛型类型背后的机制不能用于这些类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM