- •Тема 6. Основы программирования на языке c#
- •Условная операция
- •Операции checked и unchecked
- •Операция поглощения null
- •Безопасность типов
- •Преобразования типов
- •Неявные преобразования
- •Явные преобразования
- •Упаковка и распаковка
- •Проверка равенства объектов
- •Виртуальный метод Equals()
- •Статический метод Equals()
- •Проверка типов значений на равенство
- •Перегрузка операций
- •Как работают операции
- •Пример перегрузки операции: структура Vector
- •Добавление дополнительных перегрузок
- •Перегрузка операций сравнения
- •Пользовательские приведения
- •Реализация пользовательских приведений
- •Приведение между классами
- •Приведение между базовым и производным классами
- •Упаковывающие и распаковывающие приведения
- •Множественные приведения
Упаковывающие и распаковывающие приведения
Предыдущая дискуссия была сосредоточена на приведении между базовым и производным классом, когда оба относятся к ссылочным типам. Те же принципы остаются в силе и для типов значений, хотя в этом случае невозможно просто копировать ссылки должно также происходить некоторое копирование данных.
Разумеется, объявить наследника структуры или примитивного типа значений невозможно. Поэтому приведение между базовой и унаследованной структурой неизменно означает приведение между примитивным типом или структурой с одной стороны и 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.
При использовании упаковки и распаковки важно понимать, что оба процесса на самом деле копируют данные в новый упакованный или распакованный объект. Поэтому манипуляции с упакованным объектом, например, никак не влияют на содержимое исходного типа значений.
