В чем разница между боксом/распаковкой и приведением типов?
Часто эти термины кажутся взаимозаменяемыми.
В чем разница между боксом/распаковкой и приведением типов?
Часто эти термины кажутся взаимозаменяемыми.
Упаковка относится к преобразованию типа значения, не допускающего значение NULL, в ссылочный тип или преобразованию типа значения в некоторый интерфейс, который он реализует (скажем, int
в IComparable<int>
). Кроме того, преобразование базового типа значения в тип, допускающий значение NULL, также является преобразованием упаковки. (Предостережение: в большинстве дискуссий на эту тему игнорируются последние два типа конверсий.)
Например,
int i = 5;
object o = i;
преобразует i
в экземпляр типа object
.
Распаковка относится к явному преобразованию экземпляра object
или ValueType
в тип, не допускающий значения NULL, преобразование типа интерфейса в тип значения, не допускающего значение NULL (например, IComparable<int>
в int
). Кроме того, преобразование типа, допускающего значение NULL, в базовый тип также является преобразованием распаковки. (Предостережение: в большинстве дискуссий на эту тему игнорируются последние два типа конверсий.)
Например,
object o = (int)5;
int i = (int)o;
преобразует целое число, заключенное в o
, в экземпляр типа int
.
Приведение типа — это явное преобразование выражения в заданный тип. Таким образом
(type) expression
явно преобразует expression
в объект типа type
.
Nullable<T>
. Приведение типа значения к Nullable<T>
(он же T?
) НЕ является примером упаковки/распаковки, поскольку ссылочный тип не создается. Например, int? x = 5
является примером приведения типа, а НЕ бокса.
- person drwatsoncode; 12.01.2013
Упаковка и распаковка — это подмножество приведений типов. Упаковка — это обработка типа значения как ссылочного типа (что на практике включает копирование содержимого этого типа значения (из стека) в кучу и возврат ссылки на этот объект). Это позволяет передавать тип значения везде, где ожидается совместимый ссылочный тип. Он также позволяет выполнять вызовы виртуальных методов и другие функции ссылочных типов для типа значения. Распаковка является обратной этой операции (получение типа значения из упакованного объекта).
Преобразование типа — это термин, используемый для любого преобразования переменной определенного типа в другую. Это более широкое понятие.
Несколько минут назад я ответил на связанный с этим вопрос, касающийся этой разницы. Подводя итог, я классифицировал различные типы инструкций IL, сгенерированных оператором приведения C#:
box
IL) и распаковка (инструкция unbox
IL)dynamic_cast<Type>
в C++, для проверки используется инструкция castclass
IL)static_cast<Type>
в C++, существует множество инструкций IL для различных типов приведения типов между примитивными типами)op_XXX
).int*
, он по-прежнему неизменяем. И это не просто 4 байта в куче. Он имеет информацию о типе и вызывает для него виртуальные методы: благодаря этому object o = 5; string s = o.ToString();
работает. Это невозможно сделать с помощью простого int*
. В C++ это включает в себя vtables,... и бокс тоже должен обрабатывать эти вещи.
- person mmx; 07.07.2009
Бокс — это термин для преобразования типа значения (int, double, float, Guid и т. д.) в ссылочный тип (System.Object, System.String и т. д.). Выполнение этой операции упаковки выделяет память в куче (которую сборщик мусора в конечном итоге должен будет освободить). Распаковка — это процесс, обратный этому процессу: он берет ссылочный тип и превращает его в тип значения.
Приведение типов берет тип (скажем, System.Object) и обрабатывает его как другой тип (скажем, System.String).
Когда вы упаковываете что-то в C#, вы приводите это к другому типу. Разница в том, что при создании нового ссылочного типа выделяется дополнительная память.
Итог: бокс — это особый вид приведения, который преобразует тип значения в ссылочный тип, что требует выделения нового ссылочного типа.
IComparable<int> i = 5
тоже приводит к боксу. Во-вторых, boxing
— не единственное приведение, требующее выделения памяти. Определяемые пользователем приведения также могут делать это бесплатно. В-третьих, как указал Джейсон, куча — это деталь реализации. Важно обращаться с типом значения по семантике ссылочного типа, а не там, где он размещен.
- person mmx; 06.07.2009
Упаковка/распаковка и приведение типов — это две разные операции, однако они используют один и тот же синтаксис.
Они взаимозаменяемы только тогда, когда человек, говорящий об этом, не знает, что происходит на самом деле...
Упаковка — это сохранение типа значения в виде объекта в куче, а распаковка — чтение значения из объекта. Вы можете только распаковать значение как его точный тип.
Приведение — это когда вы конвертируете базовый тип в другой базовый тип (например, из int
в long
) или когда вы меняете тип ссылки (например, из List<int>
в IEnumerable<int>
).
Упаковка означает преобразование переменной типа значения (т. е. целого числа) в ссылочный тип. Распаковка обратна этому, используя приведение типов. Короче говоря, в мире .NET все происходит от типа «объект».
Например (пример С#):
int myInt = 0; // original variable (unboxed to begin with)
object boxed = myInt; // box it up
int myIntUnBoxed = (int)boxed; // and unbox it again using type casting
Вывод из этого — унификация системы типов, позволяющая рассматривать типы-значения как ссылочные типы. В этой статье более подробно рассматривается упаковка/распаковка.