Найкращий спосіб зрозуміти це - подивитися на мови програмування нижчого рівня, на яких працює C #.
У мовах найнижчого рівня, таких як C, всі змінні переходять на одне місце: Стек. Кожен раз, коли ви оголошуєте змінну, вона переходить у стек. Вони можуть бути лише примітивними значеннями, такими як bool, байт, 32-бітний int, 32-бітова uint тощо. Стек є простим і швидким. Як додані змінні, вони просто переходять один на інший, тому перший, який ви оголошуєте, сидить у скажімо, 0x00, наступний у 0x01, наступний у 0x02 в оперативній пам’яті тощо. Крім того, змінні часто попередньо розглядаються під час компіляції- час, тому їх адреса відома ще до того, як ви навіть запустите програму.
На наступному рівні вгору, як C ++, вводиться друга структура пам'яті під назвою Heap. Ви все ще живете в Стек, але в стек можна додати спеціальні вставки під назвою Покажчики , які зберігають адресу пам'яті для першого байта Об'єкта, і що Об'єкт живе в Купі. Купа - це безлад і дещо дорого в обслуговуванні, тому що на відміну від змінних Stack вони не збираються лінійно вгору, а потім вниз при виконанні програми. Вони можуть приходити і виходити без певної послідовності, і вони можуть рости і скорочуватися.
Справлятися з покажчиками важко. Вони є причиною протікання пам’яті, перевиконання буфера та розладу. C # на допомогу.
На більш високому рівні, C #, вам не потрібно думати про вказівники - .Net фреймворк (написаний на C ++) думає про це для вас і представляє їх вам як Посилання на об'єкти, а для продуктивності дозволяє зберігати більш прості значення як булі, байти та ints як типи значень. Під кришкою об'єкти та речі, які створюють клас, надходять на дорогу кучу, керовану пам’яттю, тоді як типи значень переходять у той самий стек, який у вас був на низькому рівні С - надшвидкий.
З метою збереження взаємодії між цими двома принципово різними концептами пам'яті (та стратегіями зберігання) простою з точки зору кодера, типи значень можуть бути розміщені в будь-якому часі. Бокс призводить до того, що значення буде скопійовано зі Стек, поміщено в Об'єкт та розміщено на Купі - дорожча, але, взаємодія з рідиною з Референтним світом. Як вказують інші відповіді, це станеться, коли ви, наприклад, скажете:
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, яка була скопійована в Купу. Ми можемо перевірити o на null, оскільки bool був Boxed і поміщений туди.
Як правило, вам слід уникати боксу, якщо він вам не потрібен, наприклад, щоб передавати аргумент int / bool / що завгодно як об'єкт. Є деякі основні структури в .Net, які все ще вимагають передавати Типи значень як об'єкт (і так вимагає Boxing), але здебільшого вам ніколи не потрібно буде Box.
Невичерпний перелік історичних структур C #, які потребують боксу, яких слід уникати:
Виявляється, система Event має стан перегонів у наївному використанні, і вона не підтримує асинхронізацію. Додайте до проблеми боксу, і, мабуть, цього слід уникати. (Ви можете замінити його, наприклад, системою подій async, яка використовує Generics.)
Старі моделі Threading і Timer вимушували Box за своїми параметрами, але були замінені асинхронізуванням / очікуванням, які набагато чистіші та ефективніші.
Колекції .Net 1.1 повністю покладалися на Boxing, тому що вони вийшли до Generics. Вони все ще б’ються навколо у System.Collections. У будь-якому новому коді ви повинні використовувати колекції з System.Collections.Generic, які крім уникнення боксу також забезпечують більш високу безпеку типу .
Вам слід уникати декларування або передачі Ваших типів цінності як об'єктів, якщо вам не доведеться мати справу з вищезазначеними історичними проблемами, які викликають бокс, і ви хочете уникнути удару Boxing про ефективність пізніше, коли ви знаєте, що він буде в будь-якому випадку бокс.
За пропозицією Мікаеля нижче:
Зробити це
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 тощо викликати бокс, адже насправді вони є простими псевдонімами для Value Types. Тобто .Net має такі типи, як Bool, Int32, String і C #, псевдоніми їх на bool, int, string, без будь-якої функціональної різниці.