Русская Википедия:Упаковка и распаковка значимых типов
Шаблон:К удалению Шаблон:Содержание справа Упаковка и распаковка значимых типов в C# — доступный для программиста механизм преобразования размерных типов данных языка C# из значимых в ссылочные и обратно через задействование свойств фундаментального базового класса Object.
Описание
Как правило, значимые типы предпочтительнее для использования, чем ссылочные: для них не нужно заботиться о динамическом выделении памяти из «кучи», на них не расходуются ресурсы при сборке мусора, по отношению к ним нет необходимости использовать адресацию через указатели. Однако, иногда необходимо использовать значимые типы совместно со ссылочными[1]. Помимо этого, для использования ряда библиотечных классов нередко возникает нужда в ссылках на экземпляры значимых типов, что заставляет применять механизм их упаковки (Шаблон:Lang-en) с перспективой распаковки (Шаблон:Lang-en) в будущем[2].
Упаковка
В языке C# упаковка работает как неявное преобразование экземпляра любого размерного типа в объект базового класса Object, которое происходит, когда компилятор наталкивается на значимый тип в том контексте, где ожидается появление ссылки. Это преобразование автоматически производится библиотекой CLR причём его выполнение не зависит от того, какой именно тип передан как входной — ссылочный или значимый[3].
Трансформация значимого типа в ссылочный осуществляется автоматически, для этого выполняются следующие шаги[2]:
- выделение необходимых ресурсов памяти для размещения нового объекта в «куче». Объёмы этой памяти определяются длиной упаковываемого типа и размером двух служебных членов. Первый из них является указателем на объект-тип, второй — индексом SyncBlockIndex. Этими метаданными снабжаются любые объекты в области динамической памяти.
- поля значимого типа копируются в динамически выделенную память.
- возвращается адрес полученного объекта в виде ссылочного типа.
Распаковка
Распаковка ссылочного типа в значимый подразумевает, что это должно быть выполнено явно. При этом, необходимо во-первых, сначала удостовериться, что тип упакованного объекта по ссылке соответствует исходному, а во-вторых, скопировать поля данных упакованного объекта в новую переменную данного типа. Как правило, проверку соответствия типов осуществляют с помощью механизма генерирования и обработки исключений[3], после чего копирование переносит внутренние данные (поля) объекта из «кучи» в стек выполняемого приложения, где хранятся его локальные переменные. Последовательность конкретных действий сводится к следующим шагам[2]:
- если служебный указатель на упакованный значимый тип имеет значение null, то генерируется исключение NullReferenceException,
- если упакованный объект не соответствует требуемому типу, то выбрасывается исключение InvalidCastException.
Отмечается, что распаковка не является противоположностью упаковки в строгом смысле этого слова, она гораздо менее ресурсоёмка если состоит только в запросе указателя на исходный значимый тип. Однако, в большинстве встречающихся приложений, невозможно обойтись без корректного копирования полей объекта из «кучи» в стек, что может крайне отрицательно сказаться на производительности[2].
В связи с этим отмечается, что в некоторых диалектах C++, таких как, например C++/CLI предусмотрены встроенные средства для упаковки значимого типа данных не прибегая к созданию копии его полей[2].
Примечания
Ссылки