从 派生的每个类型的定义,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 中泛型类型背后的机制不能用于此类类型。