Варианты использования для упаковки типа значения в C #? - PullRequest
10 голосов
/ 22 июня 2009

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

Понимание общей системы типов .NET

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

Ответы [ 9 ]

14 голосов
/ 22 июня 2009

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

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

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

13 голосов
/ 22 июня 2009

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

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

9 голосов
/ 22 июня 2009

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

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

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

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

5 голосов
/ 22 июня 2009

Я бы порекомендовал вам 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% разработчиков приложений не должно волновать, почему типы значений находятся в стеке, а не в куче, и какой прирост производительности мы можем получить здесь. Джуты имеют в виду очень простые правила:

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

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

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

2 голосов
/ 22 июня 2009

Мне кажется, хороший пример бокса в c # встречается в неуниверсальных коллекциях, таких как ArrayList .

1 голос
/ 22 июня 2009

код

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

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

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

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

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

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

1 голос
/ 22 июня 2009

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

1 голос
/ 22 июня 2009

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

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
1 голос
/ 22 июня 2009

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

...