Frage Gibt das Unboxing nur einen Zeiger auf den Wert innerhalb des Boxed-Objekts auf dem Heap zurück?


ich dieser MSDN Magazin Artikel, so der Autor (Hervorhebung von mir):

Beachten Sie, dass beim Boxen immer ein neues erstellt wird   Objekt und kopiert die ungepackten Werte   Bits zum Objekt. Andererseits,   Beim Unboxing wird einfach ein Zeiger auf zurückgegeben   die Daten innerhalb eines Boxed-Objekts: Nein   Speicherkopie tritt auf. Wie auch immer es ist   in der Regel der Fall, dass Ihr Code wird   verursachen die Daten, auf die von der angezeigt wird   Unboxed-Referenz, die sowieso kopiert werden soll.

Ich bin verwirrt von dem Satz, den ich geschrieben habe und dem Satz, der darauf folgt. Von allem, was ich gelesen habe, einschließlich diese MSDN-SeiteIch habe noch nie gehört, dass das Unboxing nur einen Zeiger auf den Wert auf dem Heap zurückgibt. Ich hatte den Eindruck, dass das Unboxing dazu führen würde, dass Sie eine Variable mit einer Kopie des Wertes auf dem Stapel haben, so wie Sie es begonnen haben. Wenn meine Variable schließlich "einen Zeiger auf den Wert auf dem Heap" enthält, dann habe ich keinen Werttyp, ich habe einen Zeiger.

Kann jemand erklären, was das bedeutet? War der Autor auf Crack? (Es gibt mindestens einen weiteren eklatanten Fehler in dem Artikel). Und wenn das wahr ist, was sind die Fälle, in denen "Ihr Code dazu führt, dass die Daten, auf die die nicht eingebettete Referenz verweist, trotzdem kopiert werden"?

Ich habe gerade gemerkt, dass der Artikel fast 10 Jahre alt ist. Vielleicht hat sich das schon sehr früh im Leben von .Net geändert.


8
2018-06-05 00:53


Ursprung


Antworten:


Der Artikel ist korrekt. Es spricht jedoch darüber was Ja wirklich geht weiter, nicht wie die IL aussieht, die der Compiler erzeugt. Schließlich führt ein .NET-Programm niemals IL aus, es führt den Maschinencode aus, der vom JIT-Compiler aus der IL generiert wird.

Und der Unbox-Opcode erzeugt tatsächlich einen Code, der einen Zeiger auf die Bits auf dem Heap erzeugt, der den Werttyp Wert darstellt. Der JIT generiert einen Aufruf an eine kleine Hilfsfunktion in der CLR namens "JIT_Unbox". clr \ src \ vm \ jithelpers.cpp, wenn Sie den SSCLI20-Quellcode erhalten haben. Die Object :: GetData () - Funktion gibt den Zeiger zurück.

Von dort wird der am häufigsten verwendete Wert zuerst in ein CPU-Register kopiert. Was dann irgendwo gespeichert werden kann. Es muss nicht der Stapel sein, es könnte ein Mitglied eines Objekts vom Referenztyp (der GC-Heap) sein. Oder eine statische Variable (Loader-Heap). Oder es könnte auf den Stapel geschoben werden (Methodenaufruf). Oder das CPU-Register könnte unverändert verwendet werden, wenn der Wert in einem Ausdruck verwendet wird.

Klicken Sie während des Debuggens mit der rechten Maustaste auf das Editorfenster und wählen Sie "Zur Demontage wechseln", um den Maschinencode anzuzeigen.


6
2018-06-05 06:01



Der Autor des Originalartikels muss sich darauf bezogen haben, was auf der IL-Ebene passiert. Es gibt zwei Unboxing-Opcodes: unbox und unbox.any.

Laut MSDN bezüglich unbox.any:

Wenn auf die Boxform von a angewendet   Werttyp, die Anweisung unbox.any   extrahiert den darin enthaltenen Wert   obj (vom Typ O) und ist daher   Entspricht der Unbox, gefolgt von ldobj.

und bezüglich unbox:

[...] unbox muss nicht kopiert werden   der Werttyp aus dem Objekt.   Normalerweise berechnet es einfach die   Adresse des Werttyps, der ist   schon vorhanden in der Box   Objekt.

Der Autor wusste also wovon er sprach.

Diese kleine Tatsache über unbox ermöglicht es, bei der direkten Arbeit mit IL einige raffinierte Optimierungen vorzunehmen. Wenn Sie beispielsweise einen Box-Int haben, den Sie an eine Funktion übergeben müssen, die einen Ref-Int akzeptiert, können Sie einfach einen ausgeben unbox Opcode, und der Verweis auf den Int wird im Stapel bereit sein, damit die Funktion ausgeführt werden kann. In diesem Fall wird die Funktion den tatsächlichen Inhalt des Box-Objekts ändern, was auf C # -Ebene ziemlich unmöglich ist. Es erspart Ihnen, Platz für eine temporäre lokale Variable zuzuweisen, das int dort auszugeben, einen Verweis an das int an die Funktion zu übergeben und dann ein neues Box-Objekt zu erstellen, um das int neu zu laden und das alte Feld zu verwerfen.

Wenn Sie auf C # -Ebene arbeiten, können Sie natürlich keine solchen Optimierungen vornehmen. Was normalerweise passiert, ist, dass der vom Compiler generierte Code fast immer die Variable aus dem Boxed-Objekt kopiert, bevor sie weiter verwendet wird davon.


5
2018-05-31 04:16



Beim Boxen wird eine wertetypische Instanz in eine Instanz vom Referenztyp umgewandelt (entweder ein object oder eine Schnittstelle), und Referenztypen werden auf dem Heap zugeordnet.

Laut "C # 4.0 in Kürze": "... Unboxing Kopien der Inhalt des Objekts zurück in eine Werttyp-Instanz "und das bedeutet auf dem Stapel.

In dem Artikel, auf den Sie verweisen, erklärt der Autor:

public static void Main() {

   Int32 v = 5;    // Create an unboxed value type variable
   Object o = v;   // o refers to a boxed version of v
   v = 123;        // Changes the unboxed value to 123

   Console.WriteLine(v + ", " + (Int32) o);    // Displays "123, 5"
}

Aus diesem Code können Sie erraten, wie viele   Box-Operationen auftreten? Du könntest sein   überrascht, dass die Antwort zu entdecken   ist drei! Lassen Sie uns den Code analysieren   sorgfältig, um wirklich zu verstehen, was ist   geht weiter.         Zunächst wird ein Int32-Werttyp (v) ohne Box erstellt und initialisiert   5. Anschließend wird ein Objektreferenztyp (o) erstellt, der auf v verweisen soll.   Referenztypen müssen jedoch immer zeigen   zu Objekten im Heap, also C #   erzeugt den richtigen IL-Code zu Box v   und speicherte die Adresse des Boxed   Version von v in o. Jetzt ist 123 unboxed   und die referenzierten Daten werden in kopiert   der ungepackte Werttyp v; Das hat nein   Effekt auf die Box-Version von v, so   Die Box-Version behält ihren Wert von   5. Beachten Sie, dass dieses Beispiel zeigt, wie o entkoppelt ist (wodurch ein Zeiger auf zurückgegeben wird)   die Daten in o), und dann die Daten in o   Der Speicher wird auf den Wert ohne Box kopiert   Geben Sie v ein.


1
2018-06-05 01:10