Где в памяти хранятся обнуляемые типы? - PullRequest
31 голосов
/ 19 мая 2010

Возможно, это продолжение вопроса о обнуляемых типах .

Где именно в памяти хранятся типы значений, допускающие значения NULL (int? ...)? Сначала я подумал, что это достаточно ясно, поскольку Nullable<T> является структурой, а это типы значений. Затем я нашел статью Джона Скита " Память в .NET ", в которой говорится:

Обратите внимание, что переменная типа значения может никогда не иметь значение ноль - это не имеет никакого смысла, так как нуль понятие ссылочного типа, означающее " значение этой переменной ссылочного типа не ссылка на какой-либо объект в все».

Я немного растерялся после прочтения этого заявления. Допустим, у меня есть int? a = null;. Поскольку int обычно является типом значения, хранится ли он как-то внутри структуры Nullable<T> в стеке (я использовал «обычно», потому что я не знаю, что происходит с типом значения, когда он становится обнуляемым)? Или что-нибудь еще происходит здесь - возможно, в куче?

Ответы [ 4 ]

71 голосов
/ 19 мая 2010

Прежде всего, Nullable<int> это просто сокращение для чего-то вроде:

struct Nullable<T> 
{
    bool hasValue;
    T value;
}

Плюс все конструкторы, аксессоры и так далее. Вот и все - обнуляемый int - это обычный int плюс флаг, который говорит, является ли int нулевым или нет. Все остальное - магия компилятора, которая рассматривает «ноль» как допустимое значение; все, что "null" делает с обнуляемым типом, делает вас одной из тех структур с флагом, установленным в false.

Так что теперь, когда у нас есть это вне пути, ваш вопрос "куда они идут в памяти"? Они идут в том же месте, что и любые другие структуры в памяти: где среда выполнения и компилятор считают лучшим местом, учитывая время жизни памяти.

Большинство структур идут в кучу. Любой, кто говорит вам, что «структуры всегда идут в стек», на самом деле не знает, о чем они говорят; наша документация не говорит об этом, и это не так. Структуры отправляются только во временный пул памяти, иначе говоря, «стек», когда они являются локальными переменными или временными переменными, и локальные переменные не являются закрытыми внешними переменными анонимного метода или лямбды, а локальные переменные не находятся в итераторе блок. Все другие структуры идут в кучу в нашей реализации.

Обратите также внимание, что нет требования, чтобы реализация CLI использовала «стек» для создания своего временного пула. Например, классический диспетчер памяти JScript сохраняет свой временный пул в куче. (Хотя, конечно, движок JScript не является реализацией CLI; я просто указываю, что можно разработать управляемый движок, который вообще не помещает пользовательские данные в «стек».) Логически это структура данных стека. , но эта структура данных не хранится в «стеке», это просто структура стека, выделенная в куче.

Я должен спросить: почему тебя это волнует? CLR управляет памятью от вашего имени. Почему тебя волнует, куда идут обнуляемые типы? Они идут туда, где живут достаточно долго, чтобы быть полезными для вас; вам не нужно беспокоиться об этом.

7 голосов
/ 19 мая 2010

См. Комментарий Эрика о том, где хранятся структуры. Это гораздо более тщательно (и правильно), чем то, что я написал.

Способ, которым это работает, состоит в том, что nullable имеет логическое значение, которое говорит, было ли установлено значение. Технически вы правы, обнуляемый (int, bool) и т. Д. На самом деле не равен нулю. у него есть значение по умолчанию. просто теперь у типа nullable есть логическое значение, которое вы можете проверить и посмотреть, установлено ли это значение или это просто значение по умолчанию. = и == переопределяются для установки соответствующих значений при присвоении null (и значения) ему

MSDN ссылка на Nullables

Другая ссылка, описывающая, как работают nullables:
http://www.markzhou.com/blog/post/2010/01/27/Why-can-assign-e2809cnulle2809d-to-nullable-types.aspx

3 голосов
/ 19 мая 2010

Nullables только притворяются нулевыми:

int? a = null;
Debug.Assert((a == null) == (!a.HasValue));

Компилятор замешан в этой шараде. Одним забавным следствием этого является следующее:

Nullable<double> b = new Nullable<double>();
Debug.Assert(b == null); //I'm null upon construction!

Они также получают специальную поддержку бокса:

int? c = null;
Object d = c;
Debug.Assert(d == null);
Debug.Assert(!c.HasValue);
2 голосов
/ 19 мая 2010

В дополнение к ответу Кевина: компилятор знает о типах Nullable и изменяет ==null проверку на вызов ".HasValue".

...