Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Операции и приведения.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
200.2 Кб
Скачать

Упаковывающие и распаковывающие приведения

Предыдущая дискуссия была сосредоточена на приведении между базовым и производ­ным классом, когда оба относятся к ссылочным типам. Те же принципы остаются в силе и для типов значений, хотя в этом случае невозможно просто копировать ссылки  должно также происходить некоторое копирование данных.

Разумеется, объявить наследника структуры или примитивного типа значений невоз­можно. Поэтому приведение между базовой и унаследованной структурой неизменно озна­чает приведение между примитивным типом или структурой с одной стороны и System.Object  с другой (теоретически можно выполнить приведение между структурой и System.ValueType, хотя трудно представить, зачем это может понадобиться).

Приведение любой структуры (или примитивного типа) к типу object всегда выпол­няется неявно, потому что это, по сути  приведение производного типа к базовому, что представляет собой уже известный процесс упаковки. Для примера возьмем структуру Currency:

Currency balance = new Currency(40,0);

object baseCopy = balance;

Когда выполняется это неявное приведение, содержимое balance копируется в кучу, в упакованный объект, и на этот объект устанавливается объектная ссылка baseCopy. Что же на самом деле происходит за кулисами этого процесса? Когда мы изначально определяем структуру Currency, среда .NET Framework неявно создает другой (скрытый) класс  класс-упаковку Currency, который содержит все те же поля, что исходная структура Currency, но является ссылочным типом, размещаемым в куче. Это случается всякий раз, когда опре­деляется новый тип значения  будь то перечисление или структура; подобные упакован­ные тссылочные типы существуют для всех примитивных типов значений, таких как int, double, uint и т.д. Получать прямой доступ к какому-либо классу-упаковке в исходном коде невозможно, да и не нужно, однако они являются объектами, которые работают “за кулиса­ми” при каждом приведении типа значения к object. Когда Currency неявно приводится к object, при этом создается упакованный экземпляр Currency, который инициализиру­ется данными из структуры Currency. В последнем примере это будет упакованный экзем­пляр Currency, на который ссылается baseCopy. Благодаря такому механизму, существует возможность приведения от производного к базовому типу, которое синтаксически работа- ет одинаково и для ссылочных типов, и для типов значений.

Обратное приведение известно как распаковка. Так же, как в случае приведения между базовым ссылочным типом и производным ссылочным типом, такое приведение является явным, потому что генерируется исключение, если преобразуемый объект относится к не­допустимому типу:

object derivedObject = new Currency(40,0);

object baseObject = new object();

Currency derivedCopy1 = (Currency)derivedObject; // нормально

Currency derivedCopy2=(Currency)baseObject; // генерирует исключение

Этот код работает точно так же, как приведенный ранее пример со ссылочными типами. Приведение derivedObject к Currency работает правильно, потому что derivedObject на самом деле ссылается на упакованный экземпляр Currency  приведение осуществля­ется копированием полей упакованного объекта Currency в новую структуру Currency. Второе приведение завершается неудачей, поскольку baseObject не ссылается на упако­ванный объект Currency.

При использовании упаковки и распаковки важно понимать, что оба процесса на самом деле копируют данные в новый упакованный или распакованный объект. Поэтому мани­пуляции с упакованным объектом, например, никак не влияют на содержимое исходного типа значений.