In .Net:
Spesso non puoi fare affidamento sul tipo di variabile che consumerà una funzione, quindi devi utilizzare una variabile oggetto che si estende dal minimo comune denominatore - in .Net questo è object
.
Tuttavia object
è una classe e memorizza i suoi contenuti come riferimento.
List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value
List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int
Mentre entrambi contengono le stesse informazioni, il secondo elenco è più grande e più lento. Ogni valore nel secondo elenco è in realtà un riferimento a un object
che contiene il int
.
Questo si chiama boxed perché int
è racchiuso da object
. Quando il cast viene restituito, int
viene deselezionato - convertito nuovamente nel suo valore.
Per i tipi di valore (ovvero tutti structs
), questo è lento e potenzialmente utilizza molto più spazio.
Per i tipi di riferimento (cioè tutti classes
) questo è molto meno un problema, poiché sono comunque memorizzati come riferimento.
Un ulteriore problema con un tipo di valore inscatolato è che non è ovvio che hai a che fare con la scatola, piuttosto che con il valore. Quando si confrontano due, structs
si confrontano i valori, ma quando si confrontano due, classes
quindi (per impostazione predefinita) si confronta il riferimento, ovvero si tratta della stessa istanza?
Questo può essere fonte di confusione quando si tratta di tipi di valore inscatolati:
int a = 7;
int b = 7;
if(a == b) // Evaluates to true, because a and b have the same value
object c = (object) 7;
object d = (object) 7;
if(c == d) // Evaluates to false, because c and d are different instances
È facile aggirare:
if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals
if(((int) c) == ((int) d)) // Evaluates to true once the values are cast
Tuttavia è un'altra cosa a cui fare attenzione quando si ha a che fare con valori inscatolati.