2011-01-26 2 views
4

Были пара последних вопросов о боксе ValueType в качестве объекта, в частности, произошли ли это в некоторых случаях.Бокс против ValueType по ссылке; Какая разница?

Что-то, что я понял, я не знаю, в чем разница между «боксом» ValueType (рассматривающим его как ссылочный объект) и просто доступом к нему по ссылке, например, используя ключевые слова ref или out (где то, что вы проходят только как «указатель»)? В обоих случаях значение находится где-то, где вы можете указать на него (для объекта это куча, для локального значения ValueType это ... где именно?).

Если бы мне пришлось угадать, из того, что я знаю о C++, я бы сказал, что он работает следующим образом: ValueType, к которому обращается ссылка (скажем, через ключевое слово параметра), остается на уровне стека вызовов, для которого он облагается , но указатель «shortcut» на ведро этой переменной в стеке создается и становится частью следующего слоя стека. Поскольку значение уже сохранено в памяти (возможно, даже в кэше процессора), вам не нужно создавать что-то новое в куче; единственной новой вещью является указатель, который является его собственным ValueType (IntPtr) и сам хранится в стеке, поэтому AFAIK будет быстрее, чем положить что-то в кучу.

Это то, что происходит, или есть что-то еще?

EDIT: Больше ясности:

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); 
+0

Это поможет, если вы можете привести полные примеры двух сценариев, о которых вы говорите. Очень непонятно, что вы имеете в виду в данный момент. –

ответ

3

Ссылки на классы могут быть свободно скопированы и разрешены к существованию неограниченно долго. Их можно рассматривать как идентификаторы объектов, которые хранятся (всегда) как автономные элементы в куче. Чтобы избежать злоупотребления термином «ссылка», мне нравится думать о них как о ObjectID.

Когда процедура принимает параметр по ссылке, эта ссылка является особым типом вещи, которая находится за пределами обычной системы классов (я буду называть ее ParameterReference). В отличие от ObjectID, который может существовать неограниченно, параметр ParameterReference разрешен только для продолжительности вызываемой функции. Кроме того, в отличие от ObjectID, который всегда содержит ссылку на отдельный объект, ParameterReference содержит ссылку на локальную переменную в стеке, поле внутри класса Object, элемент внутри массива или поле внутри структуры, которая сама соответствует одному из этих описаний. Если ParameterReference указывает на локальную переменную, эта переменная перестанет существовать после ее выхода из области видимости; попытка использования параметра ParameterReference после этого времени может привести к повреждению данных. Поскольку объем переменной гарантированно расширяется, по крайней мере, до тех пор, пока вызывается вызываемая процедура, и поскольку параметр ParameterReference перестанет существовать в это время, нет никакой опасности, что ParameterReference получит доступ к переменной, которая больше не существует.

+0

Хороший ответ. В значительной степени то же, что и у Тедда, но гораздо более подробно.Я позволю другим голосам решить, кто из вас получит всплеск, но большой палец для обоих я. – KeithS

+0

KeithS: Спасибо. Как я уже отмечал, я считаю, что термин «ссылка» путается, поэтому, надеюсь, использование «ObjectID» делает вещи более ясными. Кстати, я хочу, чтобы .net разрешал делать больше с «настоящими» ссылками, например, возвращал свойство value-type. Многие жалобы Эрик Липперт и др. Кажется, что об изменчивых структурах действительно жалуются на трудность иметь надлежащим образом функционирующие ссылки на них. – supercat

-1

Вы близки. Когда тип значения помещается в бокс, он копируется в структуру объекта, которая живет на куче GC. Структура объекта имеет обычную преамбулу для объектов, а биты, которые приходят после этого, представляют собой структуру типа blitted value. Когда он распаковывается, эта структура восстанавливается в стек.

+0

Что делать, если значение распаковано в поле экземпляра в классе?Что делать, если значение распаковывается в переменную, захваченную в итераторе или анонимном делетете? и т. д. – jason

+0

@ Джейсон - идите сами и ответьте сами, тогда, если вы хотите охватить каждый случай. Однако этот ответ ориентирован не на общий случай, но случай, который я понимаю, задает вопрос. Иногда меньше. – codekaizen

+0

В рамках вопроса, который я задавал (локальный вариант, который был помещен в бокс против ссылки), ответ правильный. Однако это еще не все. Я уже знал, что бокс сделал с типом значения, что я хотел знать, так это то, как передача valuetype по ссылке отличалась от бокса/распаковки. – KeithS

1

TakesAnObject(myInt); Необходимо установить флажок, потому что он получен как тип объекта (ссылочный тип). Если целью был тип значения, он был бы скопирован вместо этого.

TakesAnIntValueType(ref myInt); - ссылка на ту же область памяти, что и myInt, поэтому при изменении «оба изменения». Если бы не ref, значение было бы скопировано вместо этого.

+0

Это то, что я думал; все еще есть ссылка, но в случае значения valuetype, передаваемого по ссылке вместо того, чтобы быть помещенным в бокс, ссылка на существующую переменную в стеке. При вставке в кучу создается экземпляр нового объекта, значение копируется, а затем копируется из кучи в стек при выходе из функции. Экономия заключается в создании объекта и процессе копирования; сделать указатель дешевым. – KeithS

Смежные вопросы