2010-09-18 7 views
6

Eine einfache Frage, aber ich habe keine definitive Antwort auf Stack Overflow gefunden.Ist eine C# -Struktur jemals eingerahmt, wenn sie als Rückgabewert einer Funktion verwendet wird?

struct foo { int x; int y; int z; } 

    foo Func() 
    { 
     return new foo(); 
    } 
    void Func2() 
    { 
     foo f = Func();  // did boxing and unboxing occur? 
    } 

Ist ein C# struct (Werttyp) immer auf den Stack kopiert, wenn sie von einer Funktion zurückgegeben, egal wie groß es sein könnte? Der Grund, warum ich unsicher bin, ist, dass für einige Befehlssätze außer MSIL (wie x86) ein Rückgabewert normalerweise in ein Prozessorregister passen muss und der Stapel nicht direkt involviert ist.

Wenn ja, ist es die Aufruf-Site, die Speicherplatz auf dem CLR-Stapel für den (erwarteten) Rückgabewert-Typ reserviert?

[edit: Zusammenfassung der Antworten:] Für die Absicht der ursprünglichen Frage ist die Antwort nein; Die CLR wird eine Struktur niemals (still) boxen, nur um sie als Rückgabewert zu senden.

+0

@Brian: danke, das merke ich, aber ich denke, du sagst, dass der Prozess, es von der Funktion selbst zurückzugeben, kein Boxen beinhaltet. Ich habe den Code aktualisiert, um die Frage klarer zu machen. –

+0

Großartig, danke für deine Antworten! –

+0

Wenn eine Rückgabe eines Werttyps immer vorhanden ist (Ihre Verwendung von 'Ever' scheint manchmal zu bedeuten, dass es nicht bedingungslos ist), bedeutet das implizites Unboxing, wenn wir die Rückgabe einer Werttypvariablen zuweisen. und das scheint ineffizient. Ich weiß nicht viel über MSIL und CLR, also kann ich dir hier nicht helfen. Wie auch immer, kann ich wissen, was deine Frage ausgelöst hat? – blizpasta

Antwort

6

Es ist ein schweres Implementierungsdetail des JIT-Compilers. Im Allgemeinen, wenn die Struktur klein genug ist und einfache Mitglieder hat, wird sie in CPU-Registern zurückgegeben. Wenn es zu groß wird, reserviert der aufrufende Code genügend Speicherplatz auf dem Stapel und übergibt einen Zeiger auf diesen Speicherplatz als ein zusätzliches verstecktes Argument.

Es wird nie eingerahmt, außer der Rückgabetyp der Methode ist Objekt natürlich.

Fwiw: das ist auch der Grund, dass der Debugger den Rückgabewert der Funktion in dem Autos Fenster nicht anzeigen kann. Manchmal schmerzhaft. Der Debugger erhält jedoch nicht genügend Metadaten vom JIT-Compiler, um genau zu wissen, wo er den Wert finden kann. Bearbeiten: in VS2013 behoben.

6

Eine Struktur wird eingerahmt, wenn Sie sie als object behandeln möchten. Wenn Sie also Func aufrufen und das Ergebnis dem Objekt zuweisen, wird es eingerahmt.

z. dabei ist

object o = Func(); 

wird ergeben folgende IL

L_0000: call valuetype TestApp.foo TestApp.Program::Func() 
L_0005: box TestApp.foo 
L_000a: stloc.0 

, die zeigt, dass der Rückgabewert eingerahmt wird, weil wir es mit einer Referenz des object zuordnen.

Wenn Sie es einer Variablen vom Typ Foo zuweisen, wird es nicht eingerahmt und somit wird es kopiert und der Wert wird auf dem Stapel gespeichert.

Auch hier würde Boxen nicht wirklich helfen, da es ein Objekt erstellen würde, um den Wert der Struktur darzustellen, und die Werte werden während des Boxvorgangs effektiv kopiert.

+1

Die Tatsache, dass die Aufruf-Site die entsprechende Anzahl von Bytes auf dem Stapel zuordnen muss, ist der Grund, warum die .NET-Entwurfsrichtlinien vorschlagen, dass 'struct' idealerweise nicht größer als 16 Bytes sein sollte. –

+1

@Richard: Haben Sie eine Quelle für diese Informationen? –

+0

Check out http://msdn.microsoft.com/en-us/library/y23b5415(VS.71).aspx –

Verwandte Themen