2010-09-18 16 views
6

Proste pytanie, ale nie znalazłem ostatecznej odpowiedzi na przepełnienie stosu.Czy struktura C# jest kiedykolwiek zapakowana w ramkę, gdy jest używana jako wartość zwracana przez funkcję?

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

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

Czy C# struct (typ wartości) zawsze kopiowane do stosu po powrocie z funkcji, bez względu na to, jak duży może być? Powodem, dla którego nie jestem pewien, jest to, że dla niektórych zestawów instrukcji innych niż MSIL (takich jak x86), wartość zwracana zwykle musi pasować do rejestru procesora, a stos nie jest bezpośrednio zaangażowany.

Jeśli tak, to czy witryna wywoławcza wstępnie alokuje miejsce na stosie CLR dla oczekiwanego typu zwracanej wartości?

[edytuj: podsumowanie odpowiedzi:] Dla zamysłu pierwotnego pytania odpowiedź brzmi: nie; CLR nigdy (po cichu) nie będzie umieszczał struktury w celu wysłania jej jako wartości zwracanej.

+0

@Brian: dziękuję, zdaję sobie z tego sprawę, ale myślę, że mówisz, że proces zwrotu z samej funkcji nie wiąże się z boksowaniem. Zaktualizowałem kod, aby pytanie było jaśniejsze. –

+0

Świetnie, dzięki za odpowiedzi! –

+0

Jeśli powrót typu wartości jest kiedykolwiek (twoje użycie "kiedykolwiek" zdaje się sugerować, że czasami, jeśli to się zdarza, to nie jest bezwarunkowe) w pudełku, będzie to oznaczać, że istnieje niejawne rozpakowanie, gdy przypiszemy powrót do zmiennej typu wartości, i to wydaje się nieskuteczne. Nie wiem prawie nic o MSIL i CLR, więc nie mogę ci tu pomóc. W każdym razie, czy mogę wiedzieć, co skłoniło cię do postawienia pytania? – blizpasta

Odpowiedz

6

Jest to trudny szczegół implementacji kompilatora JIT. Ogólnie rzecz biorąc, jeśli struktura jest wystarczająco mała i ma prostych członków, to jej zwraca się w rejestrach procesora. Jeśli jest zbyt duży, kod wywołujący rezerwuje wystarczającą ilość miejsca na stosie i przekazuje wskaźnik do tej przestrzeni jako dodatkowy ukryty argument.

Nigdy nie będzie w pudełku, chyba że typem zwrotu metody jest obiekt oczywiście.

Fwiw: jest to również powód, dla którego debugger nie może wyświetlić wartości zwracanej przez funkcję w oknie Autos. Czasami bolesne. Ale debugger nie otrzymuje wystarczającej ilości metadanych z kompilatora JIT, aby dokładnie wiedzieć, gdzie znaleźć wartość. Edycja: poprawiono w VS2013.

6

Struktura jest zapakowana, gdy chcesz ją traktować jako object, więc jeśli zadzwonisz pod numer Func i przypiszesz wynik do obiektu, zostanie on zapakowany.

E.g. robi to

object o = Func(); 

przyniesie następujące IL

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

którego wynika, że ​​wartość zwracana jest zapakowane, ponieważ możemy przypisać go do odniesienia typu object.

Jeśli przypiszesz ją zmiennej typu Foo, nie jest ona zapakowana w pudełku, dlatego jest kopiowana, a wartość jest przechowywana na stosie.

Boks nie pomógłby ci tutaj, ponieważ wymagałoby to stworzenia obiektu reprezentującego wartość struktury, a wartości zostały skutecznie skopiowane podczas operacji boksu.

+1

Fakt, że strona wywoławcza musi przydzielić odpowiednią liczbę bajtów na stosie, jest powodem, dla którego wytyczne projektowania .NET sugerują, że 'struct's nie powinien być większy niż 16 bajtów. –

+1

@Richard: Czy masz źródło tych informacji? –

+0

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