2010-06-05 4 views
7

I this MSDN Magazine article, der Verfasser (Hervorhebung von mir):Liefert Unboxing nur einen Zeiger auf den Wert innerhalb des Boxed-Objekts auf dem Heap?

Beachten Sie, dass immer Boxen erstellt ein neues Objekt und kopiert die Bits des unboxed Wert auf das Objekt. Auf der anderen Seite, Unboxing gibt einfach einen Zeiger auf die Daten innerhalb eines Boxed-Objekts: keine Speicherkopie auftritt. Es ist jedoch häufig der Fall, dass Ihr Code verursachen die Daten, die durch die ungeboxten Referenz auf jeden Fall kopiert werden.

Ich bin verwirrt durch den Satz, den ich fett habe und den Satz, der darauf folgt. Von allem, was ich gelesen habe, einschließlich this MSDN page, habe ich 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 festgestellt, dass der Artikel fast 10 Jahre alt ist, also ist dies etwas, das sich sehr früh im Leben von .Net geändert hat.

Antwort

6

Der Artikel ist korrekt. Es spricht jedoch darüber, was wirklich geht weiter, nicht, wie die IL, die der Compiler erzeugt aussieht. 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 generiert in der Tat einen Code, der einen Zeiger auf die Bits auf dem Heap erzeugt, die 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 zuerst gelieferte Wert 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 Debuggings mit der rechten Maustaste auf das Editorfenster und wählen Sie "Zur Demontage wechseln", um den Maschinencode anzuzeigen.

+0

Ich sehe! Vielen Dank. –

1

Boxing ist die Aktion, bei der eine Werttypinstanz in eine Instanz vom Referenztyp (entweder object oder eine Schnittstelle) umgewandelt wird und Referenztypen auf dem Heap zugewiesen werden.

Nach "C# 4.0 in Kürze": "... unboxing kopiert den Inhalt des Objekts zurück in eine Werttyp-Instanz" und das bedeutet auf dem Stapel.

In dem Artikel Sie, der Verfasser verweisen:

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" 
} 

Von diesem Code können Sie erraten, wie viele Boxen Operationen auftreten? Sie könnten überrascht sein zu entdecken, dass die Antwort drei ist! Lassen Sie uns den Code sorgfältig analysieren, um wirklich zu verstehen, was los ist. Zuerst wird ein nicht gebundener Int32-Werttyp (v) erstellt und auf initialisiert. 5. Anschließend wird ein Objektreferenztyp (o) erstellt, der auf v zeigen soll. Referenztypen müssen jedoch immer auf Objekte im Heap auf zeigen , so erzeugte C# den richtigen IL-Code zu Box v und speicherte die Adresse der Box Version von v in o. Jetzt ist 123 entkoppelt und die referenzierten Daten werden in der ungetasteten Werttyp v kopiert; das hat keinen Effekt auf der verpackten Version von v, also behält die verpackte Version seinen Wert von 5. Beachten Sie, dass dieses Beispiel zeigt, wie o unboxed ist (das einen Zeiger auf die Daten in 0 zurückgibt), und dann das Daten in 0 ist Speicher auf den ungeboxten Wert Typ v kopiert.

+0

Ja, alles, was ich gelesen habe sagt oder impliziert, dass die Daten zurück auf den Stapel kopiert werden. Was diese Erklärung angeht, die sich widerspricht: Ja, und der Teil des Zitats, den Sie fettgedruckt haben, ist tatsächlich, worauf ich mich bezog, als ich sagte, dass ein Fehler in dem Artikel vorlag. Es ist absolut falsch - o wird im Beispiel nicht in v entpackt. –

5

Der Autor des ursprünglichen Artikels muss sich darauf beziehen, was auf der IL-Ebene passiert. Es gibt zwei Unboxing-Opcodes: unbox und unbox.any.

Nach MSDN, regarding unbox.any:

Wenn an die geschachtelte Form eines Typs Wert angewendet, der unbox.any Anweisung extrahiert die innerhalb enthaltenen Wert obj (vom Typ O) und ist deshalb entspricht der Entpackung gefolgt von ldobj.

und regarding unbox:

[...] unbox ist nicht erforderlich den Werttyp aus dem Objekt zu kopieren. In der Regel wird einfach die -Adresse des Werttyps berechnet, der bereits innerhalb des eingerahmten Objekts vorhanden ist.

So wusste der Autor, worüber 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 unbox Opcode ausgeben, und der Verweis auf den Int ist im Stack bereit, 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.

Natürlich, wenn Sie auf der C# -Ebene arbeiten, können Sie solche Optimierungen nicht tun, was normalerweise passiert, ist, dass der vom Compiler generierte Code fast immer die Variable aus dem Boxed-Objekt kopieren wird einen weiteren Gebrauch davon machen.

+0

+1 Großer Zusatz, danke. –

Verwandte Themen