Лучший способ понять это - взглянуть на языки программирования более низкого уровня, на которых строится C #.
В языках самого низкого уровня, таких как C, все переменные находятся в одном месте: стек. Каждый раз, когда вы объявляете переменную, она попадает в стек. Они могут быть только примитивными значениями, такими как bool, byte, 32-bit int, 32-bit uint и т. Д. Стек является простым и быстрым. Когда переменные добавляются, они просто идут одна поверх другой, поэтому первое, что вы объявляете, находится, скажем, в 0x00, следующее в 0x01, следующее в 0x02 в ОЗУ и т. Д. Кроме того, переменные часто предварительно адресуются при компиляции. время, поэтому их адрес известен еще до того, как вы запустите программу.
На следующем уровне вверх, как в C ++, вводится вторая структура памяти, которая называется Heap. Вы по-прежнему в основном живете в стеке, но в стек можно добавить специальные целые, называемые указателями , которые хранят адрес памяти для первого байта объекта, и этот объект живет в куче. Куча - это беспорядок и несколько дорогой в обслуживании, потому что в отличие от переменных стека они не накапливаются линейно вверх, а затем вниз при выполнении программы. Они могут приходить и уходить в определенной последовательности, и они могут расти и уменьшаться.
Работать с указателями сложно. Они являются причиной утечек памяти, переполнения буфера и разочарования. C # на помощь.
На более высоком уровне, C #, вам не нужно думать об указателях. Платформа .Net (написанная на C ++) думает об этом для вас и представляет их вам как ссылки на объекты, а для производительности позволяет хранить более простые значения как bools, bytes и int как Типы Значения. Под капотом объекты и вещи, которые создают экземпляр класса, идут в дорогую, управляемую памятью кучу, в то время как типы значений идут в том же стеке, который вы имели в низкоуровневом C - сверхбыстрый.
Для упрощения взаимодействия между этими 2 принципиально различными концепциями памяти (и стратегий хранения) с точки зрения кодировщика, типы значений могут быть упакованы в любое время. Бокс заставляет копировать значение из стека, помещать в объект и помещать в кучу - более дорогое, но плавное взаимодействие с эталонным миром. Как указывают другие ответы, это произойдет, когда вы, например, скажете:
bool b = false; // Cheap, on Stack
object o = b; // Legal, easy to code, but complex - Boxing!
bool b2 = (bool)o; // Unboxing!
Яркой иллюстрацией преимущества бокса является проверка на ноль:
if (b == null) // Will not compile - bools can't be null
if (o == null) // Will compile and always return false
Наш объект o является технически адресом в стеке, который указывает на копию нашего bool b, который был скопирован в кучу. Мы можем проверить на ноль, потому что бул был в штучной упаковке и положил туда.
В общем, вы должны избегать бокса, если вам это не нужно, например, передавать int / bool / what в качестве объекта аргументу. В .Net есть некоторые базовые структуры, которые все еще требуют передачи Типов Значений как объекта (и поэтому требуют Бокса), но по большей части вам никогда не понадобится Box.
Неисчерпывающий список исторических структур C #, требующих бокса, которых следует избегать:
Система событий, как оказалось, имеет условие гонки в наивном использовании, и она не поддерживает асинхронность. Добавьте в проблему бокса, и этого, вероятно, следует избегать. (Вы можете заменить его, например, на асинхронную систему событий, которая использует Generics.)
Старые модели Threading и Timer принудительно устанавливали Box по своим параметрам, но были заменены на async / await, которые намного чище и эффективнее.
Коллекции .Net 1.1 полностью полагались на бокс, потому что они были раньше, чем Generics. Они все еще работают в System.Collections. В любом новом коде вы должны использовать Коллекции из System.Collections.Generic, которые, помимо того, что избегают Бокса, также обеспечат вам большую безопасность типов .
Вы должны избегать объявления или передачи своих Типов значений как объектов, если только вам не приходится иметь дело с вышеупомянутыми историческими проблемами, которые вызывают бокс, и вы хотите избежать снижения производительности бокса позже, когда вы знаете, что он все равно будет помещен в бокс.
Предложение Микаэля ниже:
Сделай это
using System.Collections.Generic;
var employeeCount = 5;
var list = new List<int>(10);
Не этот
using System.Collections;
Int32 employeeCount = 5;
var list = new ArrayList(10);
Обновить
Этот ответ первоначально предполагал, что Int32, Bool и т. Д. Вызывают бокс, хотя на самом деле это простые псевдонимы для типов значений. То есть .Net имеет типы, такие как Bool, Int32, String и C #, их псевдонимы - bool, int, string без какой-либо функциональной разницы.