﻿# Упаковка и распаковка в C\#

Упаковка \(boxing\) и распаковка \(unboxing\) — операции преобразования значимых типов данных в ссылочные и обратно\. Эти операции могут негативно сказываться на производительности из\-за дополнительных вычислений: выделения памяти под новый объект и копирования данных\.

## Упаковка \(boxing\)

Упаковка происходит при преобразовании значимого типа в тип _System\.Object,_ _System\.Enum_, _System\.ValueType_ или интерфейса\. Эта операция может быть выполнена явно и неявно:

```cpp
int a = 10;
object b = a;          // Неявная упаковка
object c = (object)a;  // Явная упаковка
```

Неявная упаковка происходит, когда переменная значимого типа используется в контексте, где ожидается ссылочный тип\. Примеры таких операций:

* вызов метода с параметрами ссылочного типа и аргументами значимого;
* вызов методов базового ссылочного типа у экземпляров значимых типов;
* объявление переменной ссылочного типа с инициализацией экземпляром значимого\.

Несколько примеров рассмотрены подробнее ниже\.

**Пример 1**

```cpp
struct Point : IComparable<Point>
{
  .... 
  public int CompareTo(Point point) { .... }
}

static void 
ProcessComparableItems<T>(IComparable<T> lhs,
                          IComparable<T> rhs)
{ .... }

static int Calculate(....)
{
  var firstPoint = new Point(....);
  var secondPoint = new Point(....);
  ProcessComparableItems(firstPoint, secondPoint);
  ....
}
```

Метод _ProcessComparableItems_ работает с двумя параметрами типа _IComparable<T\>_\. В то же время структура _Point_ реализует этот интерфейс\. Несмотря на это, при вызове метода _ProcessComparableItems_ с аргументами типа _Point_ каждый из них будет упакован: 

```cpp
// ProcessComparableItems(firstPoint, secondPoint);
IL_0039: ldloc.0
IL_003a: box BoxingTest.Program/Point  // <=
IL_003f: ldloc.1
IL_0040: box BoxingTest.Program/Point  // <=
IL_0045: call void 
  BoxingTest.Program::ProcessComparableItems
    <valuetype BoxingTest.Program/Point>(....)
....
```

**Пример 2**

```cpp
var dateTime = new DateTime(....);
Type typeInfo = dateTime.GetType();
```

_dateTime_ — переменная значимого типа \(_DateType_\)\. У _dateTime_ вызывается метод _GetType_, определённый в типе _System\.Object_\. Чтобы выполнить такой вызов, объект _dateTime_ придётся упаковать:

```cpp
// Type typeInfo = dateTime.GetType();
IL_0014: ldloc.0
IL_0015: box [System.Runtime]System.DateTime // <=
IL_001a: call instance class
  [System.Runtime]System.Type
  [System.Runtime]System.Object::GetType()
....
```

## Распаковка \(unboxing\)

Распаковкой называют преобразование упакованного значения обратно в значимый тип\. Операция распаковки имеет свои особенности:

* распаковка обязана осуществляться в точно такой же тип данных, который был упакован\. В случае несоответствия возникнет исключение _InvalidCastException_;
* при попытке распаковать нулевую ссылку возникнет исключение _NullReferenceException\._

**Пример** 

```cpp
double a = 1;
object b = a;
int c = (int)b;
```

Из\-за несоответствия типов при попытке распаковки переменной возникнет исключение _InvalidCastException_\. Исправленный вариант:

```cpp
int c = (int)(double)b;
```

## Дополнительные материалы

* [Хорошо ли вы помните nullable value types? Заглядываем "под капот"](https://pvs-studio.ru/ru/blog/posts/csharp/0772/)
* [Подводные камни при работе с enum в C\#](https://pvs-studio.ru/ru/blog/posts/csharp/0844/)
* [Всегда ли в C\# есть упаковка при конкатенации со строкой и интерполяции?](https://pvs-studio.ru/ru/blog/posts/csharp/1060/)
* [Диагностическое правило V3148: Casting potential 'null' value to a value type can lead to NullReferenceException\.](https://pvs-studio.ru/ru/docs/warnings/v3148/)