Бокс и распаковка: когда это придумано?

Итак, я понимаю, что такое бокс и распаковка. Когда он появляется в коде реального мира или в каких примерах это проблема? Я не могу представить что-то вроде этого примера:

int i = 123;
object o = i;           // Boxing
int j = (int)o;     // Unboxing

... но это почти наверняка чрезвычайно упрощено, и я, возможно, даже сделал бокс/распаковку, не зная об этом раньше.

Ответ 1

Это намного меньше проблемы, чем раньше, чем дженерики. Теперь, например, мы можем использовать:

List<int> x = new List<int>();
x.Add(10);
int y = x[0];

Нет необходимости в боксе или распаковке.

Раньше у нас было бы:

ArrayList x = new ArrayList();
x.Add(10); // Boxing
int y = (int) x[0]; // Unboxing

Это был мой самый распространенный опыт бокса и распаковки, по крайней мере.

Без участия дженериков я думаю, что, вероятно, я бы сказал, что отражение - это самая распространенная причина бокса в проектах, над которыми я работал. API-интерфейсы отражения всегда используют "объект" для таких вещей, как возвращаемое значение для метода, потому что у них нет другого способа узнать, что использовать.

Другая причина, которая может вас поймать, если вы не знаете об этом, - это использовать тип значения, который реализует интерфейс, и передать это значение другому методу, который имеет тип интерфейса в качестве его параметра. Опять же, дженерики делают это менее проблемой, но это может быть неприятным сюрпризом, если вы не знаете об этом.

Ответ 2

Бокс (по моему опыту) обычно возникает в этих случаях:

  • Тип значения передается методу, который принимает аргумент типа Object.
  • Тип значения добавляется к неэквивалентной коллекции (например, ArrayList).

В других случаях вы можете видеть, что бокс и unboxing - это когда вы используете отражение, поскольку API-интерфейс отображения .NET Framework сильно использует Object.

Ответ 3

Бокс/распаковка происходит, когда тип значения (например, struct, int, long) передается где-то, который принимает ссылочный тип - например, object.

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

Вы также увидите бокс, когда вы используете String.Format() и передаете ему примитивы. Это связано с тем, что String.Format() принимает объект params [], что приводит к боксу дополнительных параметров в вызове.

Использование методов отражения для вызова также может привести к боксу/распаковке, потому что API-интерфейсы отражения не имеют другого выбора, кроме как вернуть object, поскольку реальный тип неизвестен во время компиляции (а API-интерфейсы Reflection не могут быть общими). ​​

Новые общие коллекции не приводят к боксу/распаковке, поэтому поэтому предпочтительнее старые коллекции по этой причине (например, ArrayList, Hashtable и т.д.). Не говоря уже о том, что они типичны.

Вы можете избежать проблем с боксами, изменив методы, которые принимают общие объекты. Например:

public void string Decorate( object a ) // passing a value type results in boxing
{
   return a.ToString() + " Some other value";
}

против

public void string Decorate<T>( T a )
{
   return a.ToString() + " some other value";
}

Ответ 4

Вот очень противный:)

SqlCommand cmd = <a command that returns a scalar value stored as int>;

// This code works very well.
int result = (int)cmd.ExecuteScalar();

// This code will throw an exception.
uint result = (uint)cmd.ExecuteScalar();

Второй выполнить сбой, потому что он пытается распаковать Int32 в UInt32, что невозможно. Поэтому вам нужно сначала распаковать и отличить.

uint result = (uint)(int)cmd.ExecuteScalar();

Ответ 5

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

Конечно, есть случаи, когда это имеет значение. Включение дженериков в рамки 2.0 сократило множество распространенных случаев бокса из практики.

Ответ 6

Это происходит все время, когда люди не знают, что подразумевается, просто не заботятся или иногда не могут не принимать бокс в качестве меньшего эльфа.

Сильно типизированные datarows будут вставлять/распаковывать все время, когда вы получаете доступ к свойству типа значения. Кроме того, использование типа значения в качестве ссылки на интерфейс также будет включать его. Или получить делегат от метода экземпляра типа значения. (Цель делегата имеет тип Object)

Ответ 7

С появлением сильно типизированных списков и словарей, использующих generics с С# 2.0 (Visual Studio 2005), я думаю, что важность сохранения бокса /unboxing в виду была удивительно сведена к минимуму. Добавьте к этим типам с нулевым значением (int? и т.д.) И используя нулевой оператор коалесцирования (??), и это действительно не должно быть большой проблемой вообще и, вероятно, не увидит его ни в каком коде, который не является 1.1 Framework или ранее.