Ответ 1
Вот более подробное объяснение, в котором рассматривается внутренняя среда выполнения Common Language Runtime.
Сначала давайте сделаем разницу между типами значений и ссылочными типами:
- Тип значения хранится в стек, а его копия передается вызываемым методам
- Контрольное значение сохраняется в управляемой куче, и стек содержит только указатель (ссылку) на его местоположение. Местоположение, а не объект, передается вызываемым методам.
Если вы не знаете, что такое стек (не обижайтесь), это область памяти, которая содержит локальные переменные в методе и адресах функций вызывающего абонента, используемых для инструкции return
(просто для краткости и обеспечения общий ответ). При вызове метода достаточная область в стеке выделяется статически, поэтому распределение стека всегда называется статическим распределением.
Вместо этого куча представляет собой область памяти, отделенную от стека, свойство выполняющегося процесса, в котором распределение должно быть сначала потребовано операционной системе, и почему оно называется динамическое размещение ( если вы не работаете в инструкции if, например, память может не выделяться для вашего процесса, а стек всегда выделяется).
Просто, чтобы сделать окончательный пример для кучи и стека: на языках, таких как С++, объявление int[100] a;
статически выделяет 100 * 8 байтов в стеке (предполагается 64-разрядная система), а int* a = new int[100];
объявляет 8 байтов ( на 64-битных системах) в стеке И запросит еще 800 байтов в куче, если и где доступно.
Теперь поговорим о С#:
Бокс
Поскольку int является типом значения и выделяется в стеке, когда вы передаете его объекту или любому другому ссылочному типу (на самом деле нет другого ссылочного типа, из которого int может наследовать, но это общее правило) значение должен обязательно стать ссылочным типом. Таким образом, выделяется новая область в куче, объект помещается внутри нее, и стек содержит указатель на нее.
Распаковка
Как раз наоборот: когда у вас есть ссылочный тип, например объект, и хотите передать его типу значения, например int, новое значение должно быть сохранено в стеке, поэтому CLR переходит в кучу, un - загружает значение и копирует его в стек.
Другими словами
Помните примеры int[]
и int*
? Просто, когда у вас есть int
в С#, среда выполнения ожидает, что ее местоположение стека будет удерживать значение, но вместо этого, когда у вас есть object
, он ожидает, что его реальное значение будет находиться в месте кучи, на которое указывает стек.