2014-01-24 69 views
0

由于Int32是一个结构,它意味着它是一个System.ValueType(它继承System.Object),当我将一个Integer传递给一个期望Object的函数时,为什么CLR盒子呢?System.ValueType为System.Object为什么需要装箱

CLR是否假设Object始终是引用类型?

这是一个有点混乱,认为值类型“是”的对象,但是当你要通过它“作为”对象,需要框它...

我是谁是想知道的唯一一个这个?

+0

很多人都问过,但实质上ValueType更像是一个标志而不是普通类型。它的行为与其他类型不一样。对象也没有。只要接受ValueType并不完全按照您期望的方式继承。 – Magus

+0

因为传递给它的函数必须有2个版本 - 一个用于堆对象,另一个用于堆叠对象?操作代码需要不同。 – StuartLC

+0

你真的明白拳击是什么吗? –

回答

1

这不是从Object派生的类型始终是引用类型,而是Object类型的变量始终包含引用。假设您想要将实际值存储在Object中;那么你将如何决定Object值需要多大?

编译时已知值类型的变量具有可分配空间的已知大小,但无法预先调整能够“包含”任何值类型的对象大小。然后一个合乎逻辑的解决方案是让Object变量包含对盒装对象的特殊类型的引用,其中“盒子”的大小根据正在装箱的类型动态分配。

一些稍微技术说明:

对上述问题的另一个解决方案是治疗对象在内存的引用到任意位置,这样可以防止不必创建一个盒装拷贝。这就是它在C中完成的方式,例如,您可以在堆栈中创建一个指向值的指针,然后将其传递给另一个函数以供使用。这可能是非常危险的,但是,例如,如果函数决定保留该指针并在稍后的某个未定义的时间使用该函数。由于调用堆栈已经改变,该指针现在指向的东西完全不同于最初的意图,写入它几乎肯定会产生灾难性的副作用。

.NET作为托管运行时的一部分目标是提供一个“安全”的环境,使这些特定类型的故障不会发生。这种权衡的一部分是不允许直接引用堆栈内存,当你想在包含引用的变量中“持久化”一个值类型的内容时,需要装箱。这曾经是.NET 1.1集合中的一个性能问题,但在.NET 2.0中添加了泛型意味着拳击在事件发生时并不常见。

+0

谢谢丹的清晰解释。 – ConfusedCoder

+0

允许'Object'指向内存中的任何位置都不会解决任何问题;最大限度地减少拳击的东西应该是具有足够大的“任何”类型,以至少能够保持比识别对象所必需的数目少几个比特。例如,在使用64位“对象”类型时,理论上可以指定如果四位全部置位,则剩余的60位识别对象。否则,取决于这四位的含义,该对象将表示+/- 1.34078E + 154 [有3 * 2^62个这样的值]的“双”,在范围+/- 2^59等 – supercat

+0

实际上,试图以这种方式共享位会大大增加处理对象的成本,因此最好使用额外的字段。然而,除非人们想要使GC复杂化,否则可能必须使对象引用完全独立于值部分,可能使“Anything”持有对象引用加上超过8或16个字节[取决于是否想要允许'Decimal'和'GUID'来避免拳击]。当存储一个值时,Object将标识一个静态对象,说明如何解释16字节值字段。 – supercat