Упаковка и распаковка. Необходимость (преимущества) применения обобщений. Повышение типовой безопасности с помощью обобщений
Содержание
- 1. Понятие упаковки (boxing) и распаковки (unboxing)
- 2. Какая разница между использованием обобщений и приведением к типу object? Демонстрация преимуществ применения обобщений. Пример
- Связанные темы
Поиск на других ресурсах:
1. Понятие упаковки (boxing) и распаковки (unboxing)
Как известно, в .NET Framework все базовые типы (int, double, char и т.д.) представлены соответствующим классом или структурой (Integer, Double, Char и т.п.) в общей иерархической структуре классов. В вершине этой структуры лежит тип Object, к которому также можно обращаться по имени object. Это означает, что допускается объявлять переменную типа Object и использовать ее для работы с любым типом как показано ниже
... // Можно работать также через базовый класс Object Object a; object b; a = 25; // присваивание значения типа int b = "bestprog"; // присваивание значения типа string Console.WriteLine("a = {0}", a); // a = 25 Console.WriteLine("b = {0}", b); // b = bestprog // Переменной a опять присваивается значение другого типа double a = 2.88; Console.WriteLine("a = {0}", a); // a = 2.88 ...
Допускается также использовать переменную типа object в правой части оператора присваивания:
... // Переменная типа object object c; c = 'A'; // присваивается значение char char cc; // Переменная типа char cc = (char)c; // здесь нужно явное приведение типов, иначе ошибка на этапе компиляции Console.WriteLine("cc = {0}", cc); // cc = A ...
Но в этом случае нужно указывать явное приведение типов, как видно из строки
...
cc = (char)c;
...
иначе будет ошибка на этапе компиляции.
Если переменная типа object используется в левой части оператора присваивания, то компилятор выполняет так называемую упаковку. Если переменная или значение типа object используется в правой части оператора присваивания, то компилятор выполняет распаковку.
Таким образом можно дать следующие определения. Упаковка — это процесс сохранения значения простого типа (int, char, double …) в экземпляре объекта (object). Распаковка — это процесс вытягивания упакованного значения (int, double, char …) из объекта (object). Следующий пример демонстрирует различие между этими терминами:
... object a; // тип int упаковывается в тип object a = 300; // упаковка: object <= int int b; b = (int)a; // распаковка: int <= object ...
⇑
2. Какая разница между использованием обобщений и приведением к типу object? Демонстрация преимуществ применения обобщений. Пример
Как было сказано в п. 1 в программах на C# можно объявлять ссылки на тип Object, обращаясь к именам object или Object. Благодаря наследованию, переменным типа Object может быть присвоено значение любых унаследованных типов (смотрите п. 1).
Исходя из вышесказанного, можно сделать вывод, что использование типа Object может заменить обобщения. Тогда возникает резонный вопрос: зачем использовать обобщения, если они целиком могут быть заменены типом object?
Использование обобщений вместо использования типа object дает следующие преимущества:
- отсутствие явного приведения типа в операторе присваивания при использовании обобщений;
- обеспечение типовой безопасности. Ошибка неправильного приведения типов генерируется уже на этапе компиляции а не на этапе выполнения программы;
- повышение производительности. Для типа object операция присваивания выполняется дольше, поскольку происходит упаковка, распаковка.
В следующих пунктах эти преимущества рассматриваются более подробно.
⇑
2.1. Преимущество 1. Отсутствие явного приведения типа
Если используется обобщение, то не нужно выполнять явное приведение типов в операции присваивания как показано на рисунке 1.
Рисунок 1. Отличие в явном приведении к типу int между обобщением и типом object
⇑
2.2. Преимущество 2. Обеспечение типовой безопасности в обобщениях
При использовании класса object в качестве типа можно допустить ошибку, которая на этапе компиляции не будет обнаружена. Эта ошибка окажется на этапе выполнения, что неприемлемо.
На рисунке 2 реализованы такие же классы как на рисунке 1. Однако, в функции main(), для обоих классов осуществляется попытка установить значение типа double.
В случае с классом ObjectClass ошибки на этапе компиляции не возникает. Эта ошибка вызовет исключительную ситуацию на этапе выполнения.
В случае с классом GenClass<T> ошибка будет определена на этапе компиляции. Это связано с тем, что создается типизированный код с привязкой к типу int. В этом коде ошибки определяются на этапе компиляции. Это является основным преимуществом обобщений, которые повышают типовую безопасность.
Рисунок 2. Особенности выявления ошибки компилятором для обобщенного и необобщенных класса
⇑
2.3. Преимущество 3. Повышение производительности
Использование обобщенных классов дает большую производительность (быстродействие) по сравнению с необобщенными. При присвоении значения типа object другим типам и наоборот, компилятор выполняет упаковку и распаковку (смотрите п. 1). Этот процесс требует больше временных затрат чем использование обобщений. В случае с обобщениями формируется типизированный код с привязкой к конкретному типу, который выполняется быстрее.
Рисунок 3 отражает объявление двух классов ObjectClass и GenClass. В функции main() выделены фрагменты кода, в которых проявляется различие в производительности между объектами (object) и обобщениями.
Рисунок 3. Отличие в производительности выполнения кода между обобщенным классом и классом типа object. Операция присваивания для обобщенных классов выполняется быстрее
⇑
Связанные темы
- Обобщения. Основные понятия. Обобщенные классы и структуры
- Обобщенные интерфейсы. Синтаксис объявления. Реализация обобщенных интерфейсов в классах. Примеры
- Обобщенные методы в классах. Синтаксис объявления. Способы вызова
- Обобщенные делегаты
⇑