Неужели распаковка просто возвращает указатель к значению в боксевом объекте на куче?

StackOverflow https://stackoverflow.com/questions/2978647

  •  24-10-2019
  •  | 
  •  

Вопрос

я Эта статья MSDN Magazine, автор заявляет (акцент на мой):

Обратите внимание, что Boxing всегда создает новый объект и копирует биты распакованного значения в объект. С другой стороны, распаковка просто возвращает указатель на данные в объекте в штучной упаковке: копия памяти не происходит. Однако, как правило, тот случай, когда ваш код приведет к тому, что данные, на которые указывается на разбитую ссылку в любом случае.

Меня смутила предложение, которое я живет, и предложение, которое следует за ним. От всего остального, что я читал, в том числе эта страница MSDN, Я никогда раньше не слышал, что распаковка просто возвращает указатель на значение кучи. У меня сложилось впечатление, что распаковка приведет к тому, что у вас будет переменная, содержащая копию значения в стеке, так же, как вы начали. В конце концов, если моя переменная содержит «указатель на значение в куче», то у меня нет типа значения, у меня есть указатель.

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

Я только что заметил, что этой статье почти 10 лет, так что, возможно, это то, что изменилось очень рано в жизни .NET.

Это было полезно?

Решение

Статья точная. Однако он говорит о том, что В самом деле продолжается, а не то, что выглядит так, что генерирует компилятор. В конце концов, программа .NET никогда не выполняет IL, она выполняет машинный код, который генерируется из IL компилятором JIT.

И Opcode Unbox действительно генерирует код, который дает указатель на биты на куче, которая представляет значение типа значения. JIT генерирует вызов небольшой вспомогательной функции в CLR с именем "jit_unbox". Clr src vm jithelpers.cpp, если у вас есть исходный код SSCli20. Функция объекта :: getData () возвращает указатель.

Оттуда значение чаще всего сначала копируется в реестр процессора. Который затем может быть где -то хранить. Это не обязательно должен быть стек, он может быть членом объекта эталонного типа (куча GC). Или статическая переменная (куча загрузчика). Или это может быть нажат на стек (метод вызов). Или регистр процессора может использоваться как IS, когда значение используется в выражении.

Во время отладки щелкните правой кнопкой мыши окно редактора и выберите «Перейти в разборку», чтобы увидеть машинный код.

Другие советы

Автор оригинальной статьи, должно быть, ссылался на то, что происходит на уровне IL. Существуют два распаковки: unbox а также unbox.any.

Согласно MSDN, касательно unbox.any:

При применении к штучной форме типа значения, Unbox. Любая инструкция извлекает значение, содержащее в OBJ (типа O), и, следовательно, эквивалентна UNBOX с последующим LDOBJ.

а также касательно unbox:

...] Unbox не требуется для копирования типа значения из объекта. Как правило, он просто вычисляет адрес типа значения, который уже присутствует внутри коробочного объекта.

Итак, автор знал, о чем он говорит.

Этот маленький факт о unbox позволяет сделать определенные изящные оптимизации при работе непосредственно с IL. Например, если у вас есть штучная сфера, которую вам нужно передать в функцию, принимающую ссылку, вы можете просто излучать unbox OPCODE, и ссылка на INT будет готова в стеке для работы функции. В этом случае функция изменит фактическое содержание объекта бокса, что -то, что совершенно невозможно на уровне C#. Это спасает вас от необходимости распределить пространство для временной локальной переменной, распаковывать там int, перенести рефери на функцию 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 unboxed value (v). Правильный код IL для коробки V и сохранил адрес коробочной версии V в o. Теперь 123 распаковано, и ссылочные данные копируются в тип V -типа Value Value; Это не влияет на штучную версию V, поэтому в штучной версии сохраняется значение 5. Обратите внимание, что этот пример показывает, как o unboxed (что возвращает указатель на данные в O), и тогда данные в O - это память, скопированная в тип V -диапазона V.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top