4

最近有几个关于将 ValueType 装箱为对象的问题,特别是它是否在某些情况下发生。

我意识到我不知道的是,“装箱”ValueType(将其视为引用的对象)和简单地通过引用访问它之间有什么区别,例如使用 ref 或 out 关键字(您传递的只是“指针”)?在这两种情况下,该值都在您可以指向它的某个位置(对于 Object,它是堆,对于本地范围的 ValueType,它是......在哪里,究竟是什么?)。

如果我不得不猜测,根据我对 C++ 的了解,我会说它是这样工作的:通过引用访问的 ValueType(比如说通过 parameter 关键字)保持在其作用域的调用堆栈级别上,但是 "快捷方式”指向该变量在堆栈中的存储桶的指针被创建并成为堆栈下一层的一部分。因为该值已经存储在内存中(甚至可能是 CPU 缓存),所以您不必在堆上实例化新的东西;唯一的新东西是指针,它是它自己的ValueType(一个IntPtr)并且本身存储在堆栈中,所以AFAIK它会比将东西放入堆中更快。

这是正在发生的事情,还是有其他事情正在发生?

编辑:更清晰:

public void TakesAnObject(Object obj) {...}

public void TakesAnIntValueType(ref int myValue) {...}

public void AnotherIntParameterMethod(out int myValue) {...}

...

//this locally-scoped variable is simply created on the stack.
int myInt = 5;    

//Performs boxing; an Object is instantiated in the heap that holds the 
//variable value from the stack, and that is passed by ref.
TakesAnObject(myInt);

//Apparently does NOT perform boxing, but we're still dealing with a reference.
//So what's going on?
TakesAnIntValueType(myInt);

//Again created on the stack, with the default 0.
int anotherInt;

//Again, apparently no boxing, but we're dealing with a reference to anotherInt.
AnotherIntParameterMethod(anotherInt);
4

3 回答 3

3

类引用可以自由复制,并且可以无限期地存在。可以将它们视为对象的标识符,这些对象(始终)作为独立项存储在堆上。为避免过度使用“引用”一词,我喜欢将它们视为 ObjectID。

当例程通过引用接受参数时,该引用是正常类系统之外的一种特殊类型的事物(我将其称为 ParameterReference)。与可以无限存在的 ObjectID 不同,ParameterReference 只允许在被调用函数的持续时间内存在。此外,与始终保存对独立对象的引用的 ObjectID 不同,ParameterReference 保存对堆栈上的局部变量、类 Object 中的字段、数组中的项或本身的结构中的字段的引用匹配这些描述之一。如果 ParameterReference 指向一个局部变量,那么一旦超出范围,该变量将不复存在;在此之后尝试使用 ParameterReference 可能会导致数据损坏。

于 2011-01-27T00:33:29.990 回答
1

TakesAnObject(myInt); 必须装箱,因为它是作为对象类型(引用类型)接收的。如果目标是值类型,则它会被复制。

TakesAnIntValueType(ref myInt); 是对与 myInt 相同的内存区域的引用,因此如果更改“两者都更改”。如果它不是 ref,则该值将被复制。

于 2011-01-26T17:34:44.987 回答
-1

你很近。当值类型被装箱时,它被复制到一个存在于 GC 堆上的对象结构中。对象结构具有对象的通常前导码,之后的位是 blitted 值类型结构。当它被拆箱时,这个结构被重新复制到堆栈上。

于 2011-01-26T17:23:06.590 回答