Che cos'è la boxe e unboxing e quali sono i compromessi?


135

Sto cercando una risposta chiara, concisa e accurata.

Idealmente come risposta effettiva, benché i collegamenti a buone spiegazioni siano ben accetti.


2
È davvero indipendente dalla lingua?
Henk Holterman,

3
@HenkHolterman non è certamente specifico della lingua, anche se non è rilevante per tutte le lingue - la distinzione sarà irrilevante per la maggior parte delle lingue tipizzate dinamicamente, per esempio. Non sono sicuro quale tag potrebbe essere utilizzato invece - language-but-not-type-agnostic? static-language-agnostic? Non sono sicuro che SO abbia bisogno della distinzione; potrebbe essere una buona domanda per meta.
Keith,

Risposte:


189

I valori inscatolati sono strutture di dati che sono wrapper minimi attorno ai tipi primitivi *. I valori inscatolati vengono generalmente archiviati come puntatori a oggetti nell'heap .

Pertanto, i valori inscatolati utilizzano più memoria e richiedono almeno due ricerche di memoria per accedere: una volta per ottenere il puntatore e un'altra per seguire quel puntatore alla primitiva. Ovviamente questo non è il tipo di cosa che vuoi nei tuoi circuiti interni. D'altra parte, i valori inscatolati in genere giocano meglio con altri tipi nel sistema. Dal momento che sono strutture di dati di prima classe nella lingua, hanno i metadati e la struttura previsti che hanno altre strutture di dati.

In Java e Haskell le raccolte generiche non possono contenere valori non in scatola. Le raccolte generiche in .NET possono contenere valori non in box senza penalità. Laddove i generici Java vengono utilizzati solo per il controllo del tipo in fase di compilazione, .NET genererà classi specifiche per ogni tipo generico istanziato in fase di esecuzione .

Java e Haskell hanno array non boxati, ma sono decisamente meno convenienti rispetto alle altre raccolte. Tuttavia, quando sono necessarie le massime prestazioni, vale la pena un piccolo inconveniente per evitare il sovraccarico di boxe e unboxing.

* Per questa discussione, un valore di base è qualsiasi valore che può essere archiviato nello stack di chiamate , anziché archiviato come puntatore a un valore nell'heap. Spesso sono solo i tipi di macchine (ints, float, ecc.), Le strutture e talvolta le matrici di dimensioni statiche. .NET-land li chiama tipi di valore (al contrario dei tipi di riferimento). La gente di Java li chiama tipi primitivi. Gli Haskellions li chiamano semplicemente senza scatola.

** Mi sto anche concentrando su Java, Haskell e C # in questa risposta, perché è quello che so. Per quello che vale, Python, Ruby e Javascript hanno tutti esclusivamente valori inscatolati. Questo è anche noto come approccio "Tutto è un oggetto" ***.

*** Avvertenza: un compilatore / JIT sufficientemente avanzato può in alcuni casi effettivamente rilevare che un valore che è semanticamente inscatolato quando si guarda alla fonte, può essere tranquillamente un valore non in scatola in fase di runtime. In sostanza, grazie a brillanti implementatori di linguaggio, le tue scatole a volte sono gratuite.


Perché attraverso un valore inscatolato, quali sono i vantaggi del CLR o qualunque cosa ottenga i valori di inscatolamento del modulo?
Positivo

In breve (ah ah), sono solo un altro oggetto, che è sempre così conveniente. Le primitive (almeno in Java) non discendono dall'oggetto, non possono avere campi, non possono avere metodi e generalmente si comportano in modo molto diverso dagli altri tipi di valori. D'altra parte, lavorare con loro può essere molto veloce ed efficiente in termini di spazio. Quindi il compromesso.
Peter Burns,

2
Javascript ha i cosiddetti array tipizzati (nuovo UInt32Array ecc.) Che sono array di ints e float non boxati.
nponeccop,

126

da C # 3.0 In breve :

La boxe è l'atto di lanciare un tipo di valore in un tipo di riferimento:

int x = 9; 
object o = x; // boxing the int

unboxing è ... il contrario:

// unboxing o
object o = 9; 
int x = (int)o; 

72

Boxing e unboxing è il processo di conversione di un valore primitivo in una classe wrapper orientata agli oggetti (boxing) o di conversione di un valore da una classe wrapper orientata agli oggetti al valore primitivo (unboxing).

Ad esempio, in Java, potrebbe essere necessario convertire un intvalore in un Integer(boxing) se si desidera memorizzarlo in un Collectionperché le primitive non possono essere archiviate in un Collection, solo oggetti. Ma quando si desidera ripristinarlo, Collectionè possibile che si desideri ottenere il valore come un inte non un, Integerquindi si dovrebbe decomprimerlo.

Pugilato e unboxing non è intrinsecamente negativo , ma è un compromesso. A seconda dell'implementazione del linguaggio, può essere più lento e richiede più memoria rispetto al semplice utilizzo di primitive. Tuttavia, potrebbe anche consentire all'utente di utilizzare strutture di dati di livello superiore e ottenere una maggiore flessibilità nel codice.

In questi giorni, è più comunemente discusso nel contesto della funzione "autoboxing / autounboxing" di Java (e di altre lingue). Ecco una spiegazione java incentrata sull'autoboxing .


23

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 objectche contiene il int.

Questo si chiama boxed perché intè racchiuso da object. Quando il cast viene restituito, intviene 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, structssi confrontano i valori, ma quando si confrontano due, classesquindi (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.


1
In vb.net, la distinzione tra semantica di uguaglianza è più chiara, Objectnon implementa l'operatore di uguaglianza, ma i tipi di classe possono essere confrontati con l' Isoperatore; viceversa, Int32può essere utilizzato con l'operatore di uguaglianza, ma non Is. Questa distinzione rende molto più chiaro quale tipo di confronto viene fatto.
supercat

4

Boxingè il processo di conversione di un tipo di valore in un tipo di riferimento. Considerando che Unboxingè la conversione di un tipo di riferimento in un tipo di valore.

EX: int i = 123;
    object o = i;// Boxing
    int j = (int)o;// UnBoxing

Tipi di valore sono: int, chare structures, enumerations. Tipi di riferimento sono: Classes, interfaces, arrays, stringseobjects


3

Le raccolte generiche di FCL .NET:

List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>

sono stati tutti progettati per superare i problemi di prestazioni di boxe e unboxing nelle precedenti implementazioni di raccolta.

Per ulteriori informazioni, vedere il capitolo 16, CLR via C # (2a edizione) .


1

Boxe e unboxing facilitano i tipi di valore da trattare come oggetti. Boxing significa convertire un valore in un'istanza del tipo di riferimento oggetto. Ad esempio, Intè una classe ed intè un tipo di dati. La conversione intin Intè un esempio del pugilato, mentre la conversione Intin intè unboxing. Il concetto aiuta nella garbage collection, Unboxing, d'altra parte, converte il tipo di oggetto in tipo di valore.

int i=123;
object o=(object)i; //Boxing

o=123;
i=(int)o; //Unboxing.

In javascript, var ii = 123; typeof ii ritorna number. var iiObj = new Number(123); typeof iiObjritorna object. typeof ii + iiObjritorna number. Quindi questo è l'equivalente javascript del pugilato. Il valore iiObj è stato automaticamente convertito in un numero primitivo (senza casella) per eseguire l'aritmetica e restituire un valore senza casella.
PatS

-2

Come qualsiasi altra cosa, l'autoboxing può essere problematico se non usato con attenzione. Il classico è finire con una NullPointerException e non essere in grado di rintracciarla. Anche con un debugger. Prova questo:

public class TestAutoboxNPE
{
    public static void main(String[] args)
    {
        Integer i = null;

        // .. do some other stuff and forget to initialise i

        i = addOne(i);           // Whoa! NPE!
    }

    public static int addOne(int i)
    {
        return i + 1;
    }
}

Questo è solo un codice errato e non ha nulla a che fare con l'autoboxing. La variabile iviene inizializzata prematuramente. O rendilo una dichiarazione vuota ( Integer i;) in modo che il compilatore possa sottolineare che hai dimenticato di inizializzarlo, oppure attendi di dichiararlo fino a quando non ne conosci il valore.
Erickson,

Hmm, e se faccio qualcosa nel mezzo di un blocco catch try allora il compilatore mi costringerà a inizializzarlo con qualcosa. Questo non è un vero codice - è un esempio di come potrebbe accadere.
PEELY,

Cosa dimostra questo? Non c'è assolutamente alcun motivo per usare l'oggetto Integer. Invece ora devi fare i conti con un potenziale NullPointer.
Richard Clayton,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.