Использовать случаи для бокса типа значения в С#?

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

Общие сведения о системе Common Type.NET

В Wikipedia есть пример для Java. Но в С#, каковы случаи, когда нужно было бы ввести тип значения? Или был бы лучший/похожий вопрос, почему бы вам захотеть сохранить тип значения в куче (в штучной упаковке), а не в стеке?

Ответ 1

В общем, вы, как правило, не захотите боксировать свои типы значений.

Однако есть редкие случаи, когда это полезно. Например, если вам нужно настроить таргетинг на инфраструктуру 1.1, у вас не будет доступа к общим коллекциям. Любое использование коллекций в .NET 1.1 требует обработки вашего типа значений как объекта System.Object, который вызывает бокс/распаковку.

Есть еще примеры, которые могут быть полезны в .NET 2.0+. Каждый раз, когда вы хотите воспользоваться тем, что все типы, включая типы значений, могут рассматриваться как объекты напрямую, вам может потребоваться использовать бокс/распаковку. Это может быть полезно иногда, поскольку оно позволяет сохранять любой тип в коллекции (используя объект вместо T в общей коллекции), но в целом лучше избегать этого, поскольку вы теряете безопасность типов. Один случай, когда часто возникает бокс, - это когда вы используете Reflection - многие из вызовов в отражении потребуют бокса/распаковки при работе со типами значений, так как тип неизвестен заранее.

Ответ 2

Практически никогда нет веских оснований преднамеренно вводить тип значения. Почти всегда причина, по которой вводится тип значения, заключается в том, чтобы хранить его в какой-то коллекции, которая не относится к типу. Например, старый ArrayList представляет собой набор объектов, которые являются ссылочными типами. Единственный способ собрать, скажем, целые числа - это поместить их как объекты и передать их в ArrayList.

В настоящее время у нас есть общие коллекции, поэтому это не проблема.

Ответ 3

Бокс обычно происходит автоматически в .NET, когда им приходится; часто, когда вы передаете тип значения тому, что ожидает ссылочный тип. Общим примером является string.Format(). Когда вы передаете примитивные типы значений этому методу, они помещаются как часть вызова. Итак:

int x = 10;
string s = string.Format( "The value of x is {0}", x ); // x is boxed here

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

В интересном случае, когда вы используете generics в .NET, типы значений не помещаются в бокс при использовании в качестве параметров или членов типа. Что делает дженерики более эффективными, чем старый С# -код (например, ArrayList), который обрабатывает все как {object}, чтобы быть агностиком типа. Это добавляет еще одну причину использования общих коллекций, таких как List<T> или Dictionary<T,K> над ArrayList или Hashtable.

Ответ 4

Я бы порекомендовал вам 2 приятных статьи Эрика Липперта

http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

Вот цитата, которую я бы 100% согласился с

Использование стека для локальных значений ценности тип - это просто оптимизация, которая CLR выполняет от вашего имени. Соответствующая функция типов значений что у них есть семантика бытия скопированы по значению, а не то, что иногда их освобождение может быть оптимизировано время выполнения.

В 99% приложений разработчикам не стоит заботиться о том, почему типы значений находятся в стеке, а не в куче, и какое увеличение производительности может иметься здесь. Juts имеют в виду очень простые правила:

  • Избегайте бокса/распаковки, если нет необходимо использовать коллекцию обобщений. Большинство проблем возникает не тогда, когда вы определите свои собственные типы, но когда вы использовать существующие типы inproperly (определяется Microsoft или вашим коллеги)
  • Сделайте свои типы значений просто. Если вам нужна структура с 10-20 полями, я полагаю, вы были лучше создать класс. Представьте, все эти поля будут копироваться каждый раз когда вы иногда проходите мимо него функция по значению...
  • Я не думаю, что очень полезно иметь типы значений со ссылочным типом поля внутри. Подобно структуре с Строковые и объектные поля.
  • Определите, какой тип вам нужен в зависимости от требуемая функциональность, а не он должен быть сохранен. Структуры ограниченная функциональность по сравнению с классов, поэтому если структура не может обеспечить требуемая функциональность, например конструктор по умолчанию, определить класс.
  • Если что-то может выполнить любое действия с данными других типов, он обычно определяется как класс. Для структурных операций с следует определить разные типы только если вы можете наложить один тип на другой. Скажем, вы можете добавить int в double, потому что вы можете использовать int для в два раза.
  • Если что-то должно быть без гражданства, это класс.
  • Когда вы сомневаетесь, используйте ссылочные типы.: -)

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

p.s. Я познакомился с некоторыми разработчиками ASP.NET с 2-3-летним опытом, который не знает разницы между стеком и кучей.:-( Я бы не стал нанимать такого человека, если я интервьюер, но не потому, что бокс/распаковка может быть узким местом на любом из сайтов ASP.NET, которые я когда-либо видел.

Ответ 5

Я думаю, что хороший пример бокса в С# встречается в не общих коллекциях, таких как ArrayList.

Ответ 6

В одном примере, когда метод принимает параметр объекта и должен быть передан тип значения.

Ответ 7

Ниже приведены примеры бокса/распаковки

ArrayList ints = new ArrayList();
myInts.Add(1); // boxing
myInts.Add(2); // boxing

int myInt = (int)ints [0]; // unboxing

Console.Write("Value is {0}", myInt); // boxing

Ответ 8

Одна из ситуаций, когда это происходит, например, если у вас есть метод, который ожидает параметр объекта типа, и вы передаете один из примитивных типов, например int. Или если вы определяете параметр как 'ref' типа int.

Ответ 9

Код

int x = 42;
Console.Writeline("The value of x is {0}", x );

на самом деле коробки и unboxes, потому что Writeline выполняет int. Чтобы этого избежать, вы можете сделать

int x = 42;
Console.Writeline("The value of x is {0}", x.ToString());

Остерегайтесь тонких ошибок!

Вы можете объявить свои собственные типы значений, объявив свой собственный тип как struct. Представьте, что вы объявляете struct с большим количеством свойств, а затем помещаете некоторые экземпляры внутри ArrayList. Это, конечно же, их. Теперь обратитесь к одному через оператор [], отведя его к типу и задав свойство. Вы просто задали свойство на копии. Единица в ArrayList по-прежнему не изменена.

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