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

时间:2014-07-30 11:11:32

标签: c# boxing unboxing

我明白拳击是什么。值类型被装箱到对象/引用类型,然后作为对象存储在托管堆上。但我不能通过拆箱来解决问题。

取消装箱会将您的对象/引用类型转换回值类型

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

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

long d = (long)box;

它让我有一个想法,可能是运行时隐式知道“box”对象中的实际TYPE值类型。如果我是对的,我想知道这种类型信息的存储位置。

修改

由于int可隐式转换为long。这让我很困惑。

int i = 123;
long lng = i;

非常好,因为它没有涉及装箱/拆箱。

4 个答案:

答案 0 :(得分:8)

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

那是你要问的那个。它在文献中有各种各样的名字,最常见的是#34; type handle"或"方法表指针"。后者是最准确的描述,它是指向CLR在加载类型时跟踪的信息的指针。许多框架功能都依赖于它。 Object.GetType()当然。代码中的任何强制转换以及 作为运算符都会使用它。这些演员是安全的,所以你不能把狗变成猫,类型手柄提供这种保证。盒装 int 的方法表指针指向System.Int32的方法表

在仿制药推出之前,拳击在.NET 1.x中非常非常。所有常见的集合类型都存储了 object 而不是T.因此,将一个元素放入集合中需要(隐式)装箱,再次将其取出需要使用强制转换进行显式取消装箱。

为了提高效率,抖动并不需要考虑是否需要转换,这一点非常重要。因为这需要更多的工作。因此,C#语言包含了对另一种类型进行拆箱非法的规则。现在需要的只是检查类型句柄以确保它是预期类型。抖动直接将方法表指针与您案例中System.Int32的指针进行比较。并且可以直接复制对象中嵌入的值,而无需任何转换问题。非常快,尽可能快,这可以通过内联机器代码完成,无需任何CLR调用。

此规则特定于C#,VB.NET没有。这两种语言之间的典型权衡,C#的重点是速度,方便VB.NET。在拆箱时转换为另一种类型并不是一个问题,所有简单的值类型都实现了IConvertible。您可以使用Convert helper class在代码中明确地编写它:

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

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

答案 1 :(得分:4)

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

在你的例子中,你甚至不需要将它从int转换为long,因为它是一个隐式演员。

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

因此,虽然int是按值的,但实际上缺少此信息(并不重要),但是一旦您将其设置,它就会创建一个包含所有必要元数据的新对象。这也意味着虽然int只有4个字节,但是盒装int远不止于此 - 你现在有一个引用(4-8个字节),值本身(4)和元数据(包括特定类型句柄)。这与例如C ++,它允许你将任何指针转换为任何类型的指针(让你在错误地转换时处理错误)。

同样,所有按引用对象都具有此元数据。这是参考类型成本的重要组成部分,但它也是确保类型安全的手段。这也很好地显示了ArrayList int的真实价值,以及为什么int[]List<int>效率更高 - 甚至忽略了分配的成本(更重要的是收集) )堆对象和装箱和拆箱本身,4字节int可能突然是20个字节,只是因为你存储了对它的引用:)