Warum
Ein einheitliches Typsystem zu haben und Werttypen eine völlig andere Darstellung ihrer zugrunde liegenden Daten zu ermöglichen als Referenztypen ihre zugrunde liegenden Daten darstellen (z. B. ist ein int
nur ein Eimer mit zweiunddreißig Bits, der sich vollständig von einer Referenz unterscheidet Art).
Stellen Sie sich das so vor. Sie haben eine Variable o
vom Typ object
. Und jetzt hast du eine int
und du willst sie hineinstecken o
. o
ist ein Verweis auf etwas irgendwo, und das int
ist nachdrücklich kein Verweis auf etwas irgendwo (schließlich ist es nur eine Zahl). Sie tun also Folgendes: Sie erstellen ein neues Objekt, in object
dem das gespeichert werden kann, int
und weisen diesem Objekt dann einen Verweis zu o
. Wir nennen diesen Prozess "Boxen".
Wenn Sie sich also nicht für ein einheitliches Typsystem interessieren (dh Referenztypen und Werttypen haben sehr unterschiedliche Darstellungen und Sie möchten keine gemeinsame Art, die beiden "darzustellen"), brauchen Sie kein Boxen. Wenn es Ihnen nicht wichtig ist, int
den zugrunde liegenden Wert darzustellen (dh stattdessen int
auch Referenztypen zu sein und nur einen Verweis auf den zugrunde liegenden Wert zu speichern), brauchen Sie kein Boxen.
wo soll ich es benutzen
Zum Beispiel ArrayList
isst der alte Sammlungstyp nur object
s. Das heißt, es werden nur Verweise auf Dinge gespeichert, die irgendwo leben. Ohne Boxen kann man keine in eine int
solche Sammlung aufnehmen. Aber mit Boxen können Sie.
In den Tagen der Generika braucht man das nicht wirklich und kann im Allgemeinen fröhlich mitmachen, ohne über das Problem nachzudenken. Es sind jedoch einige Einschränkungen zu beachten:
Das ist richtig:
double e = 2.718281828459045;
int ee = (int)e;
Das ist nicht:
double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception
Stattdessen müssen Sie Folgendes tun:
double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;
Zuerst müssen wir das double
( (double)o
) explizit entpacken und dann in ein umwandeln int
.
Was ist das Ergebnis von Folgendem:
double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);
Denken Sie eine Sekunde darüber nach, bevor Sie mit dem nächsten Satz fortfahren.
Wenn du gesagt hast True
und False
großartig! Warte was? Dies liegt daran, dass ==
bei Referenztypen die Referenzgleichheit verwendet wird, die prüft, ob die Referenzen gleich sind, und nicht, ob die zugrunde liegenden Werte gleich sind. Dies ist ein gefährlich leichter Fehler. Vielleicht noch subtiler
double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);
wird auch gedruckt False
!
Besser zu sagen:
Console.WriteLine(o1.Equals(o2));
die dann zum Glück drucken wird True
.
Eine letzte Subtilität:
[struct|class] Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);
Was ist die Ausgabe? Es hängt davon ab, ob! Wenn a Point
ist, struct
dann ist die Ausgabe, 1
aber wenn a Point
ist, class
dann ist die Ausgabe 2
! Bei einer Boxkonvertierung wird eine Kopie des zu boxenden Werts erstellt, um den Unterschied im Verhalten zu erläutern.
boxing
und auf die Leistung sprechenunboxing
?Im .NET Framework gibt es zwei Arten von Typen - Werttypen und Referenztypen. Dies ist in OO-Sprachen relativ häufig.
Eines der wichtigen Merkmale objektorientierter Sprachen ist die Fähigkeit, Instanzen typunabhängig zu behandeln. Dies wird als Polymorphismus bezeichnet . Da wir den Polymorphismus nutzen wollen, aber zwei verschiedene Arten von Typen haben, muss es eine Möglichkeit geben, sie zusammenzubringen, damit wir mit der einen oder anderen auf die gleiche Weise umgehen können.
In früheren Zeiten (1.0 von Microsoft.NET) gab es dieses neue Hullabaloo für Generika nicht. Sie konnten keine Methode mit einem einzigen Argument schreiben, das einen Werttyp und einen Referenztyp bedienen kann. Das ist eine Verletzung des Polymorphismus. Daher wurde das Boxen als Mittel eingesetzt, um einen Werttyp in ein Objekt zu zwingen.
Wenn dies nicht möglich wäre, würde das Framework mit Methoden und Klassen übersät sein, deren einziger Zweck darin bestand, die anderen Arten des Typs zu akzeptieren. Darüber hinaus müssten Werttypen für jeden Werttyp (Bit, Byte, int16, int32 usw. usw. usw.) eine andere Methodenüberladung aufweisen, da Werttypen nicht wirklich einen gemeinsamen Vorfahren haben.
Das Boxen verhinderte dies. Und deshalb feiern die Briten den Boxing Day.
quelle
List<string>.Enumerator
zumIEnumerator<string>
ergeben ein Objekt , das hauptsächlich wie ein Klassentyp verhält, jedoch mit einer aufgebrochenEquals
Methode. Eine bessere Möglichkeit zur Besetzung sein würde , einen benutzerdefinierten Konvertierungsoperator zu rufen, sondern die Existenz einer impliziten Umwandlung verhindert , dass.List<string>.Enumerator
IEnumerator<string>
Der beste Weg, dies zu verstehen, besteht darin, sich die untergeordneten Programmiersprachen anzusehen, auf denen C # aufbaut.
In den Sprachen der untersten Ebene wie C gehen alle Variablen an eine Stelle: The Stack. Jedes Mal, wenn Sie eine Variable deklarieren, wird sie auf dem Stapel abgelegt. Sie können nur primitive Werte sein, wie ein Bool, ein Byte, ein 32-Bit-Int, ein 32-Bit-Uint usw. Der Stapel ist sowohl einfach als auch schnell. Wenn Variablen hinzugefügt werden, werden sie einfach übereinander gelegt. Der erste, den Sie deklarieren, befindet sich beispielsweise bei 0x00, der nächste bei 0x01, der nächste bei 0x02 im RAM usw. Außerdem werden Variablen beim Kompilieren häufig voradressiert. Zeit, so dass ihre Adresse bekannt ist, bevor Sie das Programm überhaupt ausführen.
In der nächsten Stufe wird wie in C ++ eine zweite Speicherstruktur namens Heap eingeführt. Sie leben immer noch hauptsächlich im Stapel, aber dem Stapel können spezielle Ints hinzugefügt werden, die als Zeiger bezeichnet werden und die Speicheradresse für das erste Byte eines Objekts speichern, und dieses Objekt befindet sich im Heap. Der Heap ist eine Art Chaos und in der Wartung etwas teuer, da sie sich im Gegensatz zu Stack-Variablen nicht linear auf und ab stapeln, wenn ein Programm ausgeführt wird. Sie können in keiner bestimmten Reihenfolge kommen und gehen, und sie können wachsen und schrumpfen.
Der Umgang mit Zeigern ist schwierig. Sie sind die Ursache für Speicherlecks, Pufferüberläufe und Frustration. C # zur Rettung.
Auf einer höheren Ebene, C #, müssen Sie nicht über Zeiger nachdenken - das .Net-Framework (in C ++ geschrieben) berücksichtigt diese für Sie und präsentiert sie Ihnen als Verweise auf Objekte. Aus Gründen der Leistung können Sie einfachere Werte speichern wie Bools, Bytes und Ints als Werttypen. Unter der Haube befinden sich Objekte und Dinge, die eine Klasse instanziieren, auf dem teuren, speicherverwalteten Heap, während Werttypen in demselben Stapel abgelegt werden, den Sie in C auf niedriger Ebene hatten - superschnell.
Um die Interaktion zwischen diesen beiden grundlegend unterschiedlichen Speicherkonzepten (und Speicherstrategien) aus Sicht eines Codierers einfach zu halten, können Werttypen jederzeit eingerahmt werden. Durch das Boxen wird der Wert vom Stapel kopiert, in ein Objekt eingefügt und auf den Haufen gelegt - teurere, aber flüssige Interaktion mit der Referenzwelt. Wie andere Antworten zeigen, geschieht dies, wenn Sie beispielsweise sagen:
Ein starkes Beispiel für den Vorteil des Boxens ist die Überprüfung auf Null:
Unser Objekt o ist technisch gesehen eine Adresse im Stapel, die auf eine Kopie unseres Bools b verweist, die auf den Heap kopiert wurde. Wir können o auf null prüfen, weil der Bool verpackt und dort abgelegt wurde.
Im Allgemeinen sollten Sie Boxen vermeiden, es sei denn, Sie benötigen es, um beispielsweise ein int / bool / was auch immer als Objekt an ein Argument zu übergeben. Es gibt einige grundlegende Strukturen in .Net, die weiterhin die Übergabe von Werttypen als Objekt erfordern (und daher Boxing erfordern), aber zum größten Teil sollten Sie niemals Boxen benötigen.
Eine nicht erschöpfende Liste historischer C # -Strukturen, für die Boxen erforderlich ist, die Sie vermeiden sollten:
Es stellt sich heraus, dass das Ereignissystem eine Race-Bedingung hat, die naiv verwendet wird, und es unterstützt keine Asynchronisierung. Fügen Sie das Boxproblem hinzu und es sollte wahrscheinlich vermieden werden. (Sie können es beispielsweise durch ein asynchrones Ereignissystem ersetzen, das Generics verwendet.)
Die alten Threading- und Timer-Modelle haben eine Box auf ihre Parameter gezwungen, wurden jedoch durch Async / Warten ersetzt, die weitaus sauberer und effizienter sind.
Die .Net 1.1-Sammlungen stützten sich vollständig auf das Boxen, da sie vor Generics kamen. Diese treten in System.Collections immer noch auf. In jedem neuen Code sollten Sie die Sammlungen von System.Collections.Generic verwenden, die Ihnen neben der Vermeidung des Boxens auch eine stärkere Typensicherheit bieten .
Sie sollten vermeiden, Ihre Werttypen als Objekte zu deklarieren oder zu übergeben, es sei denn, Sie müssen sich mit den oben genannten historischen Problemen befassen, die das Boxen erzwingen, und Sie möchten den Leistungseinbruch des späteren Boxens vermeiden, wenn Sie wissen, dass es sowieso boxen wird.
Per Mikaels Vorschlag unten:
Mach das
Nicht das
Aktualisieren
Diese Antwort schlug ursprünglich vor, dass Int32, Bool usw. Boxen verursachen, obwohl es sich tatsächlich um einfache Aliase für Werttypen handelt. Das heißt, .Net verfügt über Typen wie Bool, Int32, String und C #, die ohne funktionalen Unterschied in Bool, Int, String umgewandelt werden.
quelle
Boxen ist nicht wirklich etwas, das Sie verwenden - es ist etwas, das die Laufzeit verwendet, damit Sie Referenz- und Werttypen bei Bedarf auf die gleiche Weise behandeln können. Wenn Sie beispielsweise eine ArrayList zum Speichern einer Liste von Ganzzahlen verwendet haben, wurden die Ganzzahlen so eingerahmt, dass sie in die Objekttyp-Slots in der ArrayList passen.
Wenn Sie jetzt generische Sammlungen verwenden, verschwindet dies so ziemlich. Wenn Sie ein erstellen
List<int>
, wird kein Boxen durchgeführt - dasList<int>
kann die ganzen Zahlen direkt enthalten.quelle
Boxing und Unboxing werden speziell verwendet, um Objekte vom Werttyp als Referenztyp zu behandeln. Verschieben ihres tatsächlichen Werts auf den verwalteten Heap und Zugreifen auf ihren Wert als Referenz.
Ohne Boxing und Unboxing könnten Sie Werttypen niemals als Referenz übergeben. und das bedeutet, dass Sie keine Werttypen als Instanzen von Object übergeben konnten.
quelle
Der letzte Ort, an dem ich etwas entpacken musste, war das Schreiben von Code, der einige Daten aus einer Datenbank abrief (ich habe LINQ to SQL nicht verwendet, sondern nur altes ADO.NET ):
Wenn Sie vor Generika mit älteren APIs arbeiten, werden Sie grundsätzlich auf Boxen stoßen. Davon abgesehen ist es nicht so üblich.
quelle
Boxing ist erforderlich, wenn wir eine Funktion haben, die ein Objekt als Parameter benötigt, aber verschiedene Werttypen übergeben werden müssen. In diesem Fall müssen wir zuerst Werttypen in Objektdatentypen konvertieren, bevor wir sie an die Funktion übergeben.
Ich glaube nicht, dass das stimmt. Versuchen Sie stattdessen Folgendes:
Das läuft gut, ich habe kein Boxen / Unboxen benutzt. (Es sei denn, der Compiler macht das hinter den Kulissen?)
quelle
In .net enthält jede Instanz von Object oder ein davon abgeleiteter Typ eine Datenstruktur, die Informationen zu ihrem Typ enthält. "Echte" Werttypen in .net enthalten keine solchen Informationen. Damit Daten in Werttypen von Routinen bearbeitet werden können, die vom Objekt abgeleitete Typen empfangen, definiert das System automatisch für jeden Werttyp einen entsprechenden Klassentyp mit denselben Elementen und Feldern. Durch das Boxen werden neue Instanzen dieses Klassentyps erstellt, wobei die Felder von einer Werttypinstanz kopiert werden. Beim Unboxing werden die Felder von einer Instanz des Klassentyps in eine Instanz des Werttyps kopiert. Alle Klassentypen, die aus Werttypen erstellt werden, werden von der ironisch benannten Klasse ValueType abgeleitet (die trotz ihres Namens tatsächlich ein Referenztyp ist).
quelle
Wenn eine Methode nur einen Referenztyp als Parameter verwendet (z. B. eine generische Methode, die über die Einschränkung auf eine Klasse
new
beschränkt ist), können Sie keinen Referenztyp an sie übergeben und müssen ihn einschließen.Dies gilt auch für alle Verfahren wahr , die nehmen
object
als Parameter - dies hat ein Referenztyp sein.quelle
Im Allgemeinen sollten Sie das Boxen Ihrer Werttypen vermeiden.
Es gibt jedoch seltene Fälle, in denen dies nützlich ist. Wenn Sie beispielsweise auf das 1.1-Framework abzielen müssen, haben Sie keinen Zugriff auf die generischen Sammlungen. Für jede Verwendung der Sammlungen in .NET 1.1 muss Ihr Werttyp als System.Object behandelt werden, was zu Boxing / Unboxing führt.
Es gibt immer noch Fälle, in denen dies in .NET 2.0+ nützlich ist. Jedes Mal, wenn Sie die Tatsache nutzen möchten, dass alle Typen, einschließlich Werttypen, direkt als Objekt behandelt werden können, müssen Sie möglicherweise Boxing / Unboxing verwenden. Dies kann manchmal nützlich sein, da Sie damit jeden Typ in einer Sammlung speichern können (indem Sie Objekt anstelle von T in einer generischen Sammlung verwenden). Im Allgemeinen ist es jedoch besser, dies zu vermeiden, da Sie die Typensicherheit verlieren. Der einzige Fall, in dem Boxen häufig auftritt, ist die Verwendung von Reflection. Bei vielen Aufrufen in Reflection ist Boxing / Unboxing erforderlich, wenn mit Werttypen gearbeitet wird, da der Typ nicht im Voraus bekannt ist.
quelle
Boxing ist die Konvertierung eines Werts in einen Referenztyp, bei dem die Daten in einem Objekt auf dem Heap versetzt sind.
Was das Boxen eigentlich macht. Hier sind einige Beispiele
Mono C ++
Beim Unboxing in Mono wird ein Zeiger mit einem Versatz von 2 gpointern im Objekt (z. B. 16 Byte) umgewandelt. A
gpointer
ist avoid*
. Dies ist sinnvoll, wenn man sich die Definition von betrachtet,MonoObject
da es sich eindeutig nur um eine Kopfzeile für die Daten handelt.C ++
Um einen Wert in C ++ zu boxen, können Sie Folgendes tun:
quelle