这本MSDN杂志文章, ,作者说(强调我的):

请注意,拳击始终创建一个新对象,并将未框的值的位复制到对象。 另一方面,拆箱简单地返回盒装对象中数据的指针:不发生内存副本。 但是,通常情况下,您的代码会导致未包装引用指向的数据被复制。

我对我粗体的句子和随后的句子感到困惑。从我读过的所有内容,包括 此MSDN页面, ,我从来没有听说过拆箱只是返回指向堆上值的指针。我的印象是,拆箱将导致您在堆栈上包含一个值的变量,就像您一开始。毕竟,如果我的变量包含“指向堆上值的指针”,那么我没有一个值类型,我有一个指针。

有人可以解释这意味着什么吗?作者是裂纹吗? (本文中至少还有其他明显的错误)。如果这是真的,那么“您的代码会导致未包装引用指向的数据被复制的数据”?

我只是注意到这篇文章已经快10年了,所以也许这在.NET的生活中很早就改变了。

有帮助吗?

解决方案

文章是准确的。但是它谈论了什么 真的 继续,不是编译器生成的IL。毕竟,.NET程序从不执行IL,它执行了JIT编译器从IL生成的机器代码。

UNBOX OPCODE确实生成了代码,该代码可生成指向代表值类型值的堆上位的指针。 JIT在名为“ Jit_unbox”的CLR中生成了对小型助手功能的调用。如果您获得了SSCLI20源代码,则Clr SRC VM Jithelpers.cpp。对象:: getData()函数返回指针。

从那里,最常见的值首先被复制到CPU寄存器中。然后可能存储在某个地方。它不必是堆栈,它可以是参考类型对象(GC堆)的成员。或静态变量(装载机堆)。或者可以将其推到堆栈上(方法调用)。或在表达式中使用该值时,可以使用CPU寄存器。

调试时,右键单击编辑器窗口,然后选择“转到拆卸”以查看机器代码。

其他提示

原始文章的作者一定是指在IL级别上发生的事情。存在两个拆箱操作编码: unboxunbox.any.

根据MSDN的说法 关于 unbox.any:

当应用于值类型的盒装形式时,unbox。任何指令都提取OBJ中包含的值(O型O),因此等效于Unbox,其次是LDOBJ。

关于 unbox:

...]不需要Unbox从对象复制值类型。通常,它简单地计算盒装对象内已经存在的值类型的地址。

因此,作者知道他在说什么。

这个关于 unbox 直接与IL合作时,可以进行某些漂亮的优化。例如,如果您有一个盒装int,您需要将其传递给接受ref int的函数,则可以发出 unbox OpCode,对INT的引用将在堆栈中准备就绪,以便在堆栈中运行。在这种情况下,功能将更改拳击对象的实际内容,这在C#级别上是完全不可能的。它可以使您免于为临时局部变量分配空间,卸下int,将ref传递给int到函数,然后创建一个新的拳击对象来重新装箱,然后丢弃旧盒子。

当然,当您在c#级别上工作时,您无法进行任何此类优化,因此通常会发生的事情是,编译器生成的代码几乎总是在进行进一步使用之前从盒装对象中复制变量它。

拳击是将值型实例投射到参考类型实例的行为(要么是 object 或界面)和参考类型分配在堆上。

根据“ C#4.0”中的说法:“ ...拆箱 副本 对象的内容回到值型实例中”,这意味着在堆栈上。

在您参考的文章中,作者指出:

public static void Main() {

   Int32 v = 5;    // Create an unboxed value type variable
   Object o = v;   // o refers to a boxed version of v
   v = 123;        // Changes the unboxed value to 123

   Console.WriteLine(v + ", " + (Int32) o);    // Displays "123, 5"
}

通过此代码,您能猜测发生了多少次拳击操作吗?您可能会惊讶地发现答案是三个!让我们仔细分析代码,以真正了解发生了什么。首先,创建一个INT32未框的值类型(V)并初始化为5。然后创建了对象参考类型(O),并希望指向v。但是,参考类型必须始终指向堆中的对象,因此C#生成了适当的IL代码将框V并存储在O中V中的V版本的地址。现在,123已卸载,并将引用数据复制到未框的值类型V中;这对V的盒装版本没有影响,因此盒装版本保持其值为5。请注意,此示例显示了o over over的over(返回O中的数据指针), 然后将O中的数据复制到未框的值类型V中。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top