В чем разница между боксом/распаковкой и приведением типов?

В чем разница между боксом/распаковкой и приведением типов?

Часто эти термины кажутся взаимозаменяемыми.


person Community    schedule 06.07.2009    source источник
comment
Мне пришлось прочитать оба верхних ответа 2 здесь, чтобы получить хорошее представление о том, в чем разница, но я думаю, что теперь у меня есть довольно твердое понимание   -  person Code Jockey    schedule 15.05.2013


Ответы (5)


Упаковка относится к преобразованию типа значения, не допускающего значение 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.

person jason    schedule 06.07.2009
comment
Спасибо. Кажется, не существует стандартного языка, касающегося этих вещей. - person ; 06.07.2009
comment
Существует очень стандартный язык относительно этих вещей. См. спецификацию языка C#, ECMA #334. - person jason; 06.07.2009
comment
Для ясности, когда Джейсон говорит, что преобразование базового типа значения в тип, допускающий значение NULL, также является преобразованием упаковки, то, что он подразумевает под типом, допускающим значение NULL, является ссылочным типом, который может быть использование термина nullable. Что он НЕ имеет в виду, так это тип Nullable<T>. Приведение типа значения к Nullable<T> (он же T?) НЕ является примером упаковки/распаковки, поскольку ссылочный тип не создается. Например, int? x = 5 является примером приведения типа, а НЕ бокса. - person drwatsoncode; 12.01.2013
comment
Например инт? x = 5 является примером приведения типа, а НЕ упаковки. Для полноты, это пример приведения типа IMPLICIT, в отличие от примера явного приведения типа, который дал Джейсон. - person almulo; 20.05.2015
comment
если можно кастовать, то можно ли отменить кастинг? то есть память освобождается в куче? - person BKSpurgeon; 04.08.2017

Упаковка и распаковка — это подмножество приведений типов. Упаковка — это обработка типа значения как ссылочного типа (что на практике включает копирование содержимого этого типа значения (из стека) в кучу и возврат ссылки на этот объект). Это позволяет передавать тип значения везде, где ожидается совместимый ссылочный тип. Он также позволяет выполнять вызовы виртуальных методов и другие функции ссылочных типов для типа значения. Распаковка является обратной этой операции (получение типа значения из упакованного объекта).

Преобразование типа — это термин, используемый для любого преобразования переменной определенного типа в другую. Это более широкое понятие.

Несколько минут назад я ответил на связанный с этим вопрос, касающийся этой разницы. Подводя итог, я классифицировал различные типы инструкций IL, сгенерированных оператором приведения C#:

  1. Упаковка (инструкция box IL) и распаковка (инструкция unbox IL)
  2. Приведение по иерархии наследования (например, dynamic_cast<Type> в C++, для проверки используется инструкция castclass IL)
  3. Приведение типов между примитивными типами (например, static_cast<Type> в C++, существует множество инструкций IL для различных типов приведения типов между примитивными типами)
  4. Вызов определяемых пользователем операторов преобразования (на уровне IL это просто вызовы метода для соответствующего метода op_XXX).
person mmx    schedule 06.07.2009
comment
Мне это не очень помогает. Думаю, я проведу небольшое исследование в Интернете, чтобы найти конкретные определения для сравнения. - person ; 06.07.2009
comment
Я бы не сказал, что бокс/распаковка вообще являются подмножеством кастинга. Речь идет о перемещении типов значений в ссылочные типы, чтобы их можно было изменить/сохранить в другом месте. Где-as приведение — это процесс повторного прерывания ссылочного типа. - person Simeon Pilgrim; 06.07.2009
comment
@Simeon: с точки зрения языкового дизайна, int в штучной упаковке — это просто другой тип, и вы конвертируете между разными типами. - person mmx; 06.07.2009
comment
Где-как можно было бы думать об этом как об одном и том же типе (например, они оба являются int), но разные способы хранения int. Но тогда с фона C это просто указатели на меня. - person Simeon Pilgrim; 07.07.2009
comment
@Simeon: Это неправильный способ думать о боксе. Вероятно, это прямой артефакт мышления о различии стека/кучи для него. Как я уже сказал, семантически это просто преобразование типа значения в тип со ссылочными характеристиками. Обратите внимание, что в отличие от 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#, вы приводите это к другому типу. Разница в том, что при создании нового ссылочного типа выделяется дополнительная память.

Итог: бокс — это особый вид приведения, который преобразует тип значения в ссылочный тип, что требует выделения нового ссылочного типа.

person Judah Gabriel Himango    schedule 06.07.2009
comment
Спасибо! Вот такой ответ я ищу! - person ; 06.07.2009
comment
Куча — это деталь реализации. - person jason; 06.07.2009
comment
Эта «куча» деталей на самом деле помогает мне лучше понять это! - person ; 06.07.2009
comment
Не совсем так. Прежде всего, это не обязательно System.Object, а фактически любой ссылочный тип. Например, IComparable<int> i = 5 тоже приводит к боксу. Во-вторых, boxing — не единственное приведение, требующее выделения памяти. Определяемые пользователем приведения также могут делать это бесплатно. В-третьих, как указал Джейсон, куча — это деталь реализации. Важно обращаться с типом значения по семантике ссылочного типа, а не там, где он размещен. - person mmx; 06.07.2009
comment
Это понятно, просто будьте осторожны, чтобы ваше понимание деталей реализации не повлияло на ваше понимание семантики. Здесь важно не то, как получается, что, скажем, тип-значение становится ссылочным типом, а то, что это означает. - person jason; 06.07.2009
comment
Бывают моменты, когда я думаю, что программисты чрезмерно педантичны. Это один из тех моментов. - person Judah Gabriel Himango; 06.07.2009
comment
да. Если бы бокс не выделял новый объект в управляемой куче, это был бы уже не бокс, и Microsoft пришлось бы переписывать свою собственную документацию: msdn.microsoft.com/en-us/library/yz2be5wk.aspx - person Judah Gabriel Himango; 06.07.2009
comment
Чтобы уточнить, приведенный выше комментарий был написан в ответ на комментарий Джейсона, который, по-видимому, с тех пор был удален. - person Judah Gabriel Himango; 06.07.2009
comment
Я бы сказал, что в общем смысле «куча, означающая не память стека», должна восприниматься как должное. не только деталь реализации. то есть, если типы значений, основанные на стеке, также не являются деталями реализации, и в этот момент все эти типы значений меньше размера X также следует забыть. - person Simeon Pilgrim; 06.07.2009
comment
@Judah: Мой вопрос был следующим: если бы бокс не был реализован с использованием кучи, он бы все еще назывался боксом? Microsoft заявляет следующее на странице, на которую вы ссылаетесь: Упаковка — это процесс преобразования типа значения в объект типа или в любой тип интерфейса, реализованный этим типом значения. Когда среда CLR упаковывает тип значения, она упаковывает значение в System.Object и сохраняет его в управляемой куче. Обратите внимание, что они говорят вам, что такое бокс, без ссылки на кучу. Затем они расскажут вам, как CLR реализует бокс. Также проверьте п. 114 спецификации ECMA 334: куча не упоминается. - person jason; 06.07.2009
comment
Действительно ли нам нужно вступать в дебаты о том, что куча является деталью реализации, когда постер просто спрашивал о разнице между приведением и боксом? Мой ответ был правильным — CLR выделяет память в куче для упаковки. Вы верно указываете, хоть и педантично, что куча — это деталь реализации. Я могу возразить, но это текущая реализация, достойная внимания и вряд ли изменится в обозримом будущем. --- ЧЕСТНО --- Мы скатываемся к глупостям с таким педантичным подшучиванием. - person Judah Gabriel Himango; 07.07.2009

Упаковка/распаковка и приведение типов — это две разные операции, однако они используют один и тот же синтаксис.

Они взаимозаменяемы только тогда, когда человек, говорящий об этом, не знает, что происходит на самом деле...

Упаковка — это сохранение типа значения в виде объекта в куче, а распаковка — чтение значения из объекта. Вы можете только распаковать значение как его точный тип.

Приведение — это когда вы конвертируете базовый тип в другой базовый тип (например, из int в long) или когда вы меняете тип ссылки (например, из List<int> в IEnumerable<int>).

person Guffa    schedule 06.07.2009
comment
Я знаю это. Я спрашиваю, в чем отличия! - person ; 06.07.2009
comment
Спасибо! Вот что я искал! - person ; 06.07.2009

Упаковка означает преобразование переменной типа значения (т. е. целого числа) в ссылочный тип. Распаковка обратна этому, используя приведение типов. Короче говоря, в мире .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

Вывод из этого — унификация системы типов, позволяющая рассматривать типы-значения как ссылочные типы. В этой статье более подробно рассматривается упаковка/распаковка.

person ajawad987    schedule 06.07.2009
comment
Разве язык Basic не всегда позволял обрабатывать типы значений/ссылок одинаково? Это специфичная вещь для С#? - person ; 06.07.2009
comment
Можно сказать, что идея упаковки/распаковки пришла из языка Basic (то есть до VB.NET). Он не ограничивается C#, поскольку те же самые концепции могут быть применены к VB.NET или любому другому языку, ориентированному на среду выполнения .NET. Прошло много времени с тех пор, как я программировал на языке Basic... ах, воспоминания. - person ajawad987; 06.07.2009