运行时如何知道盒装值类型的确切类型

本文关键字:类型 何知道 盒装 运行时 | 更新日期: 2023-09-27 18:15:30

我明白拳击是什么了。值类型被装箱为对象/引用类型,然后作为对象存储在托管堆上。但是我无法打开箱子。

拆箱将对象/引用类型转换回值类型

int i = 123;          // A value type
object box = i;       // Boxing
int j = (int)box;     // Unboxing

好吧。但是,如果我试图将一个值类型拆箱为另一个值类型,例如,在上面的例子中,它会抛出InvalidCastException

long d = (long)box;

这让我有了一个想法,可能运行时隐式地知道"box"对象内的值类型的实际类型。如果我是对的,我想知道这些类型信息存储在哪里。

编辑:

因为int隐式地转换为long。这就是我困惑的地方。

int i = 123;
long lng = i;

完全没问题,因为它不涉及装箱/拆箱。

运行时如何知道盒装值类型的确切类型

当一个值被装箱时,它得到一个对象头。从System派生的任何类型的类型。对象。该值跟随该报头。头包含两个字段,一个是"syncblk",它有各种用途,超出了问题的范围。第二个字段描述对象的类型。

这就是你要问的。它在文献中有各种各样的名称,最常见的是"类型句柄"或"方法表指针"。后者是最准确的描述,它是一个指针,指向CLR在加载类型时跟踪的信息。许多框架特性都依赖于它。当然是Object.GetType()。代码中的任何强制转换以及都是,因为操作符使用它。这些类型转换是安全的,所以你不能把狗变成猫,类型句柄提供了这个保证。方框int的方法表指针指向System的方法表。Int32

装箱在。net 1中非常常见。X,在泛型可用之前。所有常见的集合类型都存储对象而不是t。因此,将元素放入集合中需要(隐式)装箱,将其取出需要通过强制转换显式拆箱。

为了使其有效,抖动不需要考虑转换的可能性是非常重要的。因为这需要更多的工作。因此c#语言包含了这样的规则,即拆箱为另一种类型是非法的。现在所需要做的就是检查类型句柄,以确保它是预期的类型。抖动直接将方法表指针与System的指针进行比较。对你来说是Int32。并且可以直接复制对象中嵌入的值,而无需考虑任何转换问题。非常快,尽可能快,这一切都可以用内联机器码完成,而不需要任何CLR调用。

这个规则是c#, VB特有的。NET没有。这两种语言之间的典型权衡,c#的重点是速度,VB。NET的便利性。当拆箱不成问题时转换为另一种类型,所有简单值类型都实现了IConvertible。您可以使用Convert helper类在代码中显式地编写它:

        int i = 123;                    // A value type
        object box = i;                 // Boxing
        long j = Convert.ToInt64(box);  // Conversion + unboxing

这是非常相似的代码,VB。. NET编译器自动生成。

这是因为装箱指令将值类型令牌添加到结果对象MSDN中。当您将值从对象中拆箱时,该变量是已知的类型(和内存中的大小)。因此,必须将对象强制转换为原始值类型。

在您的示例中,您甚至不需要将其从int强制转换为long,因为它是隐式强制转换。

这是因为当您执行装箱而不是将值类型从堆栈移动到堆时,它在堆中创建了它的副本,并将它的引用存储在堆栈中新的堆栈盒中。因此,您的原始堆栈对象,即值类型对象及其数据类型信息保留在堆栈中并维护其历史记录。现在在拆箱时,它比较堆中的对象类型与堆栈中的原始数据类型,如果发现不匹配则给出错误。因此,在进行拆箱操作时,有必要使用与装箱相同的数据类型。

每个引用对象都有一堆与之相关的元数据。这包括给定对象的确切类型(这就是为什么您可以拥有类型安全)。

所以当int是按值计算时,这个信息实际上是缺失的(这并不重要),但是一旦你把它框起来,它就会创建一个包含所有必要元数据的新对象。这也意味着,虽然int只有4个字节,但盒装的int远不止于此——您现在已经获得了引用(4-8字节)、值本身(4)和元数据(包括特定类型句柄)。这与c++非常不同,c++允许你将任何指针强制转换为任何类型的指针(当你强制转换错误时,你要处理错误)。

同样,所有按引用的对象都有这个元数据。这是引用类型成本中相当重要的一部分,但它也是确保类型安全性的手段。这也很好地显示了intArrayList是多么昂贵,以及为什么int[]List<int>更有效——即使忽略分配(更重要的是收集)堆对象以及装箱和拆箱本身的成本,4字节的int可能突然变成20字节,只是因为您存储了对它的引用:)