Является ли когда-либо структура C # упакованной в качестве возвращаемого значения функции? - PullRequest
7 голосов
/ 18 сентября 2010

Простой вопрос, но я не нашел однозначного ответа о переполнении стека.

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

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

Является ли структура C # (тип значения) всегда копируемой в стек при возврате из функции, независимо от ее размера? Причина, по которой я не уверен, заключается в том, что для некоторых наборов команд, отличных от MSIL (например, x86), возвращаемое значение обычно должно помещаться в регистр процессора, а стек напрямую не задействуется.

Если это так, то является ли сайт вызова предварительно выделяющим пространство в стеке CLR для (возвращаемого) типа возвращаемого значения?

[править: резюме ответов:] Для первоначального вопроса ответ отрицательный; CLR никогда (молча) не будет упаковывать структуру только для того, чтобы отправить ее в качестве возвращаемого значения.

Ответы [ 2 ]

7 голосов
/ 18 сентября 2010

Это тяжелая деталь реализации JIT-компилятора. В общем, если структура достаточно мала и имеет простые члены, ее возвращают в регистры процессора. Если он становится слишком большим, вызывающий код резервирует достаточно места в стеке и передает указатель на это пространство в качестве дополнительного скрытого аргумента.

Он никогда не будет упакован, если только тип возвращаемого значения метода object конечно.

Fwiw: это также причина того, что отладчик не может отобразить возвращаемое значение функции в окне Autos. Больно иногда. Но отладчик не получает достаточно метаданных от JIT-компилятора, чтобы точно знать, где найти значение. Редактировать: исправлено в VS2013.

6 голосов
/ 18 сентября 2010

Структура упаковывается, когда вы хотите обработать ее как object, поэтому, если вы вызовете Func и назначите результат объекту, он будет упакован.

Например, при этом

 object o = Func();

будет получен следующий IL

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

, который показывает, что возвращаемое значение упаковано, потому что мы присваиваем его ссылкевведите object.

Если вы присваиваете ее переменной типа Foo, она не упаковывается, поэтому она копируется и значение сохраняется в стеке.

Кроме того, в этом случае бокс вам не очень поможет, поскольку он предполагает создание объекта, представляющего значение структуры, и значения эффективно копируются во время операции упаковки.

...