Бокс и распаковка: когда это происходит? - PullRequest
14 голосов
/ 23 декабря 2009

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

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

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

Ответы [ 7 ]

32 голосов
/ 23 декабря 2009

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

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 отражения всегда используют «объект» для таких вещей, как возвращаемое значение для метода - потому что у них нет другого способа узнать, что использовать.

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

8 голосов
/ 23 декабря 2009

Бокс (по моему опыту) обычно происходит в следующих случаях:

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

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

4 голосов
/ 23 декабря 2009

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

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

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

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

Более новые универсальные коллекции не приводят к упаковке / распаковке и поэтому предпочтительнее старых коллекций по этой причине (например, 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";
}
3 голосов
/ 23 декабря 2009

Вот действительно противный:)

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();
1 голос
/ 23 декабря 2009

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

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

1 голос
/ 23 декабря 2009

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

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

0 голосов
/ 23 декабря 2009

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

...