通过引用拳击与ValueType;有什么不同?

时间:2011-01-26 17:20:28

标签: .net memory boxing

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

我意识到我不知道的是,“装箱”ValueType(将其视为引用的对象)与仅通过引用访问它有什么区别,例如使用ref或out关键字(你在哪里)传球只是一个“指针”)?在这两种情况下,值都可以指向它(对于一个对象,它是堆,对于本地范围的ValueType,它是......在哪里,确切地说?)。

如果我不得不猜测,根据我对C ++的了解,我会说它的工作方式如下:通过引用访问的ValueType(比如说通过参数关键字)保留在作为作用域的调用堆栈的级别上,但是会创建一个指向堆栈中该变量桶的“快捷方式”指针,并成为堆栈下一层的一部分。因为该值已经存储在内存中(可能甚至是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);

3 个答案:

答案 0 :(得分:3)

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

当例程通过引用接受参数时,该引用是一种特殊类型的事物,它在普通类系统之外(我将其称为ParameterReference)。与可以无限期存在的ObjectID不同,ParameterReference仅允许在被调用函数的持续时间内存在。此外,与始终保持对独立对象的引用的ObjectID不同,ParameterReference持有对堆栈上的局部变量,类Object中的字段,数组中的项或结构中的字段的引用。匹配其中一个描述。如果ParameterReference指向局部变量,那么一旦变量超出范围,该变量将不再存在;在此之后尝试使用ParameterReference可能会导致数据损坏。由于保证变量的范围至少延伸到被调用的例程退出,并且由于ParameterReference在那时不再存在,因此ParameterReference不存在访问不再存在的变量的危险。

答案 1 :(得分:1)

TakesAnObject(myInt); 必须打包,因为它是作为对象类型(引用类型)接收的。如果目标是一个值类型,那么它将被复制。

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

答案 2 :(得分:-1)

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

相关问题