C#. Упаковка и распаковка. Необходимость (преимущества) применения обобщений

Упаковка и распаковка. Необходимость (преимущества) применения обобщений. Повышение типовой безопасности с помощью обобщений


Содержание


Поиск на других ресурсах:




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.

C#. Обобщения. Отличие в явном приведении к типу int между обобщением и типом object

Рисунок 1. Отличие в явном приведении к типу int между обобщением и типом object

 

2.2. Преимущество 2. Обеспечение типовой безопасности в обобщениях

При использовании класса object в качестве типа можно допустить ошибку, которая на этапе компиляции не будет обнаружена. Эта ошибка окажется на этапе выполнения, что неприемлемо.

На рисунке 2 реализованы такие же классы как на рисунке 1. Однако, в функции main(), для обоих классов осуществляется попытка установить значение типа double.

В случае с классом ObjectClass ошибки на этапе компиляции не возникает. Эта ошибка вызовет исключительную ситуацию на этапе выполнения.

В случае с классом GenClass<T> ошибка будет определена на этапе компиляции. Это связано с тем, что создается типизированный код с привязкой к типу int. В этом коде ошибки определяются на этапе компиляции. Это является основным преимуществом обобщений, которые повышают типовую безопасность.

C#. Обобщения. Особенности выявления ошибки компилятором

Рисунок 2. Особенности выявления ошибки компилятором для обобщенного и необобщенных класса

 

2.3. Преимущество 3. Повышение производительности

Использование обобщенных классов дает большую производительность (быстродействие) по сравнению с необобщенными. При присвоении значения типа object другим типам и наоборот, компилятор выполняет упаковку и распаковку (смотрите п. 1). Этот процесс требует больше временных затрат чем использование обобщений. В случае с обобщениями формируется типизированный код с привязкой к конкретному типу, который выполняется быстрее.

Рисунок 3 отражает объявление двух классов ObjectClass и GenClass. В функции main() выделены фрагменты кода, в которых проявляется различие в производительности между объектами (object) и обобщениями.

C#. Обобщения. Производительность

Рисунок 3. Отличие в производительности выполнения кода между обобщенным классом и классом типа object. Операция присваивания для обобщенных классов выполняется быстрее

 


Связанные темы