2009-12-22 3 views
12

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

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

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

+0

Я согласен с тобой. Из всего, что я читал по этому вопросу, я просто не понимаю. Должно быть, что-то не хватает :) –

ответ

31

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

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

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

+7

«Думаю, я бы, наверное, сказал, что отражение - это самая распространенная причина бокса в проектах, над которыми я работал» - это, конечно же, будет в значительной степени зависеть от проектов. Например, если вы работаете с WPF или Silverlight, бокс будет происходить * все время * при работе со преобразователями значений (IValueConverter принимает и возвращает объект), свойства зависимостей (DependencyObject.GetValue и SetValue возвращают и берут объект) и т. Д. – itowlson

+0

+1 Для интерфейса, реализованного по типу значения - это подлый один :) –

+0

@itowlson: Это отличные примеры - если я добавлю их в свой ответ? –

8

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

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

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

+1

Нам нужно отметить: int (Int32) является подклассом абстрактного класса ValueType, который является подклассом Object. –

+0

Я бы не сказал, что это «подкласс» - это вовсе не класс *. Он наследует (или происходит от) 'ValueType'. Спецификации C# и CLI используют немного другую терминологию на этом фронте, что не помогает. –

1

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

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

+0

C# 2, а не 2.0 framework;) – disklosr

0

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

+2

Нет, в дженериках намного больше бокса происходит, чем ожидали большинство людей. C# будет испускать .box, когда вы проверяете unconstraint Ts против null. –

+0

Я согласен с этим, но это решение разработчика делать при реализации универсального метода/класса. С точки зрения структуры общие коллекции позволяют потребителю отказаться от сценария бокса/распаковки. Когда я описываю сценарий, который вы описываете, мой неограниченный T будет сравниваться с дефолтом (T), а не null. Если я * действительно * нуждаюсь в проверке против null, T лучше ограничивается ссылочным типом. –

+0

"в любом коде, который не является 1.1 Framework или ранее" - как насчет простой строки.Format ("Мой номер {0}", 123)? – Alex

4

Бокс/распаковка происходит, когда тип значения (например, 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"; 
} 
1

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

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

3

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

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(); 
+0

+1 для обеспечения примера – chikak

Смежные вопросы