2008-08-16 23 views
115

Ich bin auf der Suche nach einer klaren, präzisen und präzisen Antwort.Was ist Boxen und Unboxing und was sind die Kompromisse?

Ideal als die tatsächliche Antwort, obwohl Links zu guten Erklärungen begrüßen.

+2

Ist das wirklich sprachunabhängig? –

+3

@HenkHolterman es ist sicherlich nicht sprachspezifisch, obwohl es auch nicht für alle Sprachen relevant ist - die Unterscheidung ist beispielsweise für die meisten dynamisch typisierten Sprachen irrelevant. Ich bin nicht sicher, welcher Tag stattdessen verwendet werden könnte - "Sprache-aber-nicht-Typ-agnostisch"? 'statisch-sprach-agnostisch'? Ich bin mir nicht sicher, ob SO die Unterscheidung braucht; könnte aber eine gute Frage für Meta sein. – Keith

Antwort

170

Boxed-Werte sind data structures, die minimale Wrapper um primitive types * sind. Boxed-Werte werden typischerweise als Zeiger auf Objekte unter the heap gespeichert.

So verwenden Boxed-Werte mehr Speicher und nehmen mindestens zwei Speicherzugriffsvorgänge für den Zugriff auf: einmal, um den Zeiger abzurufen, und einen weiteren, um dem Zeiger auf das Primitiv zu folgen. Offensichtlich ist das nicht das, was du in deinen inneren Schleifen willst. Auf der anderen Seite spielen Boxed-Werte in der Regel besser mit anderen Typen im System. Da es sich um erstklassige Datenstrukturen in der Sprache handelt, haben sie die erwarteten Metadaten und die Struktur, die andere Datenstrukturen haben.

In Java und Haskell dürfen generische Sammlungen keine ungesicherten Werte enthalten. Generische Auflistungen in .NET können ungesicherte Werte ohne Strafen enthalten. Wenn Java-Generics nur für die Typüberprüfung beim Kompilieren verwendet werden, wird .NET generate specific classes for each generic type instantiated at run time.

Java und Haskell haben ungeordnete Arrays, aber sie sind deutlich weniger bequem als die anderen Sammlungen. Wenn Spitzenleistung benötigt wird, ist es jedoch eine kleine Unannehmlichkeit wert, um den Overhead von Boxen und Unboxing zu vermeiden.

* Für diese Diskussion ist ein primitiver Wert ein Wert, der unter the call stack gespeichert werden kann und nicht als Zeiger auf einen Wert auf dem Heap gespeichert wird. Häufig sind das nur die Maschinentypen (Ints, Floats usw.), Strukturen und manchmal auch statische Arrays. .NET-land nennt sie Werttypen (im Gegensatz zu Referenztypen). Java-Leute nennen sie primitive Typen. Haskellions nennen sie nur unboxed.

** Ich konzentriere mich auch auf Java, Haskell und C# in dieser Antwort, denn das ist, was ich weiß. Für das, was es wert ist, haben Python, Ruby und Javascript alle ausschließlich Boxed-Werte. Dies wird auch als "Alles ist ein Objekt" -Ansatz *** bezeichnet.

*** Vorbehalt: Ein ausreichend fortgeschrittener Compiler/JIT kann in einigen Fällen tatsächlich erkennen, dass ein Wert, der beim Betrachten der Quelle semantisch eingerahmt wird, zur Laufzeit ein Wert ohne Box sein kann. Dank brillanter Programmiersprachen sind Ihre Boxen manchmal kostenlos.

+0

Warum, obwohl ein Boxed-Wert, welchen Nutzen hat die CLR oder was immer Box-Werte? – PositiveGuy

+0

Kurz (ha ha), sie sind nur ein weiteres Objekt, das so praktisch ist. Primitive (zumindest in Java) stammen nicht von Object ab, können keine Felder haben, können keine Methoden haben und verhalten sich im Allgemeinen sehr anders als andere Arten von Werten. Auf der anderen Seite kann die Arbeit mit ihnen sehr schnell und platzsparend sein. Also der Kompromiss. –

+1

Javascript hat so genannte typisierte Arrays (neue UInt32Array etc.), die Arrays von ungetasten Ints und Floats sind. – nponeccop

66

Boxing & Unboxing ist der Prozess der Umwandlung eines primitiven Werts in eine objektorientierte Wrapper-Klasse (Boxen) oder die Konvertierung eines Werts von einer objektorientierten Wrapper-Klasse zurück in den primitiven Wert (Unboxing).

Zum Beispiel in Java, müssen Sie möglicherweise einen int Wert in eine Integer (Boxen) konvertieren, wenn Sie es in einem Collection speichern wollen, weil Primitive kann nicht in einem Collection gespeichert wird, werden nur Objekte. Aber wenn Sie es wieder aus der Collection bekommen möchten, können Sie den Wert als int und nicht Integer erhalten, so dass Sie es auspacken würden.

Boxing und Unboxing ist nicht inhärent schlecht, aber es ist ein Kompromiss. Abhängig von der Implementierung der Sprache kann es langsamer und speicherintensiver sein als nur die Verwendung von Primitiven. Es kann Ihnen jedoch auch ermöglichen, Datenstrukturen höherer Ebene zu verwenden und größere Flexibilität in Ihrem Code zu erreichen.

Heute wird es am häufigsten im Zusammenhang mit der Funktion "Autoboxing/Auto-Boxing" von Java (und anderen Sprachen) diskutiert. Hier ist ein java centric explanation of autoboxing.

23

In .Net:

Oft können Sie nicht auf das verlassen, was die Art der Variable wird verbrauchen eine Funktion, so dass Sie eine Objektvariable verwenden müssen, die von den kleinsten gemeinsamen Nenner erstreckt - in .Net dies object.

Allerdings ist object eine Klasse und speichert ihren Inhalt als Referenz.

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 

Während beide die gleiche Information enthalten, ist die zweite Liste größer und langsamer. Jeder Wert in der zweiten Liste ist eine Referenz auf eine object, die die int enthält.

Dies wird als Boxed bezeichnet, da die int durch die object gewickelt ist. Wenn es zurückgeworfen wird, wird die int ungekammert - konvertiert zurück zu ihrem Wert.

Für Werttypen (d. H. Alle structs) ist dies langsam und benötigt möglicherweise viel mehr Platz.

Für Referenztypen (d. H. Alle classes) ist dies viel weniger ein Problem, da sie sowieso als Referenz gespeichert sind.

Ein weiteres Problem mit einem Box-Wert-Typ ist, dass es nicht offensichtlich ist, dass Sie mit der Box handeln, anstatt den Wert. Wenn Sie zwei structs dann vergleichen, vergleichen Sie Werte, aber wenn Sie zwei classes dann vergleichen (standardmäßig) vergleichen Sie die Referenz - d. H. Sind diese die gleiche Instanz?

Dies kann verwirrend sein, wenn sie mit boxed Werttypen handelt:

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 

Es ist leicht zu umgehen:

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 

Doch es eine andere Sache ist, vorsichtig zu sein, wenn sie mit boxed Werte handelt.

+1

In vb.net ist die Unterscheidung zwischen Gleichheits-Semantiken klarer, "Objekt" implementiert den Gleichheitsoperator nicht, aber Klassen-Typen können mit dem "Is" -Operator verglichen werden; umgekehrt kann "Int32" mit dem Gleichheitsoperator verwendet werden, aber nicht mit "Is". Diese Unterscheidung macht es viel klarer, welche Art von Vergleich durchgeführt wird. – supercat

110

von C# 3.0 In a Nutshell:

Boxen ist der Akt der Wert Typ in einen Referenztyp Gießen:

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

Unboxing ist ... das Gegenteil:

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

+1, jetzt ist das ** prägnant **. – Sam

3

.NET FCL generic Sammlungen:

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

wurden alle entwickelt, um die Performance-Probleme des Boxens und Unboxing in früheren Sammlung Implementierungen zu überwinden.

Weitere Informationen finden Sie in Kapitel 16, CLR via C# (2nd Edition).

-2

Wie alles andere kann Autoboxieren problematisch sein, wenn es nicht sorgfältig verwendet wird. Der Klassiker ist, eine NullPointerException zu beenden und nicht in der Lage zu sein, sie aufzuspüren. Auch mit einem Debugger. Versuchen Sie dies:

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; 
    } 
} 
+0

Das ist nur schlechter Code und hat nichts mit Autoboxing zu tun. Die Variable "i" wird vorzeitig initialisiert. Entweder machen Sie es zu einer leeren Deklaration ('Integer i;'), so dass der Compiler darauf hinweisen kann, dass Sie vergessen haben, es zu initialisieren, oder warten Sie, bis Sie den Wert erkannt haben. – erickson

+0

Hmm, und wenn ich etwas in einem try catch-Block dazwischen tue, dann wird der Compiler mich zwingen, es mit etwas zu initialisieren. Dies ist kein echter Code - es ist ein Beispiel dafür, wie es passieren könnte. – fiddlesticks

+0

Was zeigt dies? Es gibt absolut keinen Grund, das Integer-Objekt zu verwenden. Stattdessen müssen Sie sich nun mit einem möglichen NullPointer beschäftigen. –

1

Boxing ist der Prozess der Konvertierung eines Werttyps in einen Referenztyp.

Unboxing ist die Konvertierung eines Referenztyps in einen Werttyp.

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

Wert Typ sind:
int, char und Strukturen, Aufzählungen. Referenztypen sind: Klassen, Schnittstellen, Arrays, Strings und Objekte

0

Boxing und Unboxing erleichtert Werttypen als Objekte behandelt werden. Boxen bedeutet, einen Wert in eine Instanz des Objektreferenztyps zu konvertieren. Beispielsweise ist Int eine Klasse und int ist ein Datentyp. Die Umwandlung von int in Int ist ein Beispiel für das Boxen, während das Konvertieren von Int in int unboxing ist. Das Konzept hilft bei der Garbage Collection, das Unboxing hingegen konvertiert Objekttyp in Werttyp.

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

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