Il modo migliore per capirlo è guardare ai linguaggi di programmazione di livello inferiore su cui C # si basa.
Nei linguaggi di livello più basso come C, tutte le variabili vanno in un posto: lo Stack. Ogni volta che dichiari una variabile, questa va in pila. Possono essere solo valori primitivi, come un valore booleano, un byte, un int a 32 bit, un uint a 32 bit, ecc. Lo Stack è sia semplice che veloce. Man mano che le variabili vengono aggiunte, vanno semplicemente una sopra l'altra, quindi la prima che dichiari si trova a dire, 0x00, la successiva a 0x01, la successiva a 0x02 in RAM, ecc. Inoltre, le variabili sono spesso pre-indirizzate durante la compilazione- ora, quindi il loro indirizzo è noto prima ancora di eseguire il programma.
Al livello successivo, come C ++, viene introdotta una seconda struttura di memoria chiamata Heap. Vivi ancora per lo più nello Stack, ma ints speciali chiamati Puntatori possono essere aggiunti allo Stack, che memorizza l'indirizzo di memoria per il primo byte di un Oggetto e quell'oggetto vive nell'Heap. L'heap è un po 'un casino e un po' costoso da mantenere, perché a differenza delle variabili Stack non si accumulano linearmente su e poi giù mentre un programma viene eseguito. Possono andare e venire in nessuna sequenza particolare e possono crescere e restringersi.
Gestire i puntatori è difficile. Sono la causa di perdite di memoria, sovraccarichi del buffer e frustrazione. C # in soccorso.
A un livello superiore, C #, non è necessario pensare ai puntatori: il framework .Net (scritto in C ++) li considera per te e li presenta come riferimenti agli oggetti e, per prestazioni, ti consente di memorizzare valori più semplici come bool, byte e ints come tipi di valore. Sotto il cofano, Oggetti e cose che istanziano una Classe vanno nel costoso Heap gestito dalla memoria, mentre i Tipi di valore vanno nello stesso Stack che avevi in C di basso livello - super veloce.
Al fine di mantenere semplice l'interazione tra questi 2 concetti fondamentalmente diversi di memoria (e strategie di archiviazione) dal punto di vista di un programmatore, i Tipi di valore possono essere inscatolati in qualsiasi momento. La boxe fa sì che il valore venga copiato dallo Stack, messo in un Oggetto e posizionato nell'Heap - interazione più costosa, ma fluida con il mondo di riferimento. Come sottolineato da altre risposte, ciò si verificherà quando ad esempio dici:
bool b = false; // Cheap, on Stack
object o = b; // Legal, easy to code, but complex - Boxing!
bool b2 = (bool)o; // Unboxing!
Un'illustrazione forte del vantaggio del pugilato è un controllo per null:
if (b == null) // Will not compile - bools can't be null
if (o == null) // Will compile and always return false
Il nostro oggetto o è tecnicamente un indirizzo nello Stack che punta a una copia del nostro bool b, che è stato copiato nell'Heap. Possiamo controllare o per null perché il bool è stato inscatolato e messo lì.
In generale, dovresti evitare la boxe a meno che tu non ne abbia bisogno, ad esempio per passare un int / bool / qualunque cosa come oggetto a un argomento. Ci sono alcune strutture di base in .Net che richiedono ancora il passaggio di Tipi di valore come oggetto (e quindi richiedono Boxing), ma per la maggior parte non dovresti mai aver bisogno di Box.
Un elenco non esaustivo di strutture storiche C # che richiedono il pugilato, che dovresti evitare:
Il sistema di eventi risulta avere una Race Race in uso ingenuo e non supporta l'asincronizzazione. Aggiungi il problema di inscatolamento e probabilmente dovrebbe essere evitato. (È possibile sostituirlo, ad esempio, con un sistema di eventi asincrono che utilizza Generics.)
I vecchi modelli di Threading e Timer hanno forzato un Box sui loro parametri ma sono stati sostituiti da asincrono / wait che sono molto più puliti ed efficienti.
Le collezioni .Net 1.1 si basavano interamente sulla boxe, perché venivano prima di Generics. Questi stanno ancora andando in giro in System.Collections. In ogni nuovo codice dovresti usare le Collezioni di System.Collections.Generic, che oltre a evitare la boxe ti offrono anche una maggiore sicurezza del tipo .
Dovresti evitare di dichiarare o trasmettere i tuoi Tipi di valore come oggetti, a meno che tu non debba affrontare i problemi storici di cui sopra che forzano il Boxing e vuoi evitare il colpo di prestazione del Boxing in un secondo momento quando sai che verrà comunque inscatolato.
Di seguito il suggerimento di Mikael:
Fai questo
using System.Collections.Generic;
var employeeCount = 5;
var list = new List<int>(10);
Non questo
using System.Collections;
Int32 employeeCount = 5;
var list = new ArrayList(10);
Aggiornare
Questa risposta originariamente suggeriva che Int32, Bool ecc. Causassero il pugilato, quando in realtà sono semplici alias per Tipi di valore. Cioè .Net ha tipi come Bool, Int32, String e C # che li alias per bool, int, string, senza alcuna differenza funzionale.