Упаковка и распаковка значимых типов
Упаковка и распаковка значимых типов в C# — доступный для программиста механизм преобразования размерных типов данных языка C# из значимых в ссылочные и обратно через задействование свойств фундаментального базового класса Object.
ОписаниеПравить
Как правило, значимые типы предпочтительнее для использования, чем ссылочные: для них не нужно заботиться о динамическом выделении памяти из «кучи», на них не расходуются ресурсы при сборке мусора, по отношению к ним нет необходимости использовать адресацию через указатели. Однако, иногда необходимо использовать значимые типы совместно со ссылочными[1]. Помимо этого, для использования ряда библиотечных классов нередко возникает нужда в ссылках на экземпляры значимых типов, что заставляет применять механизм их упаковки (англ. boxing) с перспективой распаковки (англ. unboxing) в будущем[2].
УпаковкаПравить
В языке C# упаковка работает как неявное преобразование экземпляра любого размерного типа в объект базового класса Object, которое происходит, когда компилятор наталкивается на значимый тип в том контексте, где ожидается появление ссылки. Это преобразование автоматически производится библиотекой CLR причём его выполнение не зависит от того, какой именно тип передан как входной — ссылочный или значимый[3].
Трансформация значимого типа в ссылочный осуществляется автоматически, для этого выполняются следующие шаги[2]:
- выделение необходимых ресурсов памяти для размещения нового объекта в «куче». Объёмы этой памяти определяются длиной упаковываемого типа и размером двух служебных членов. Первый из них является указателем на объект-тип, второй — индексом SyncBlockIndex. Этими метаданными снабжаются любые объекты в области динамической памяти.
- поля значимого типа копируются в динамически выделенную память.
- возвращается адрес полученного объекта в виде ссылочного типа.
РаспаковкаПравить
Распаковка ссылочного типа в значимый подразумевает, что это должно быть выполнено явно. При этом, необходимо во-первых, сначала удостовериться, что тип упакованного объекта по ссылке соответствует исходному, а во-вторых, скопировать поля данных упакованного объекта в новую переменную данного типа. Как правило, проверку соответствия типов осуществляют с помощью механизма генерирования и обработки исключений[3], после чего копирование переносит внутренние данные (поля) объекта из «кучи» в стек выполняемого приложения, где хранятся его локальные переменные. Последовательность конкретных действий сводится к следующим шагам[2]:
- если служебный указатель на упакованный значимый тип имеет значение null, то генерируется исключение NullReferenceException,
- если упакованный объект не соответствует требуемому типу, то выбрасывается исключение InvalidCastException.
Отмечается, что распаковка не является противоположностью упаковки в строгом смысле этого слова, она гораздо менее ресурсоёмка если состоит только в запросе указателя на исходный значимый тип. Однако, в большинстве встречающихся приложений, невозможно обойтись без корректного копирования полей объекта из «кучи» в стек, что может крайне отрицательно сказаться на производительности[2].
В связи с этим отмечается, что в некоторых диалектах C++, таких как, например C++/CLI предусмотрены встроенные средства для упаковки значимого типа данных не прибегая к созданию копии его полей[2].
ПримечанияПравить
- ↑ Т. А. Павловская. Упаковка и распаковка // Программирование на языке высокого уровня в C#. — М.: ИНТУИТ, 2016. — С. 32.
- ↑ 1 2 3 4 5 Дж. Рихтер. Упаковка и распаковка значимых типов // CLR via C#. Программирование на платформе Microsoft.NET Framework 4.0 на языке С#. — 3-е. — СПб.: Питер, 2012. — С. 136. — 928 с. — ISBN 978-5-459-00297-3.
- ↑ 1 2 Либерти Д. Упаковка и распаковка типов // Программирование на C#. — СПб.. — 2003: Символ-Плюс, 2003. — С. 139. — 688 с. — ISBN 5-93286-038-3.