一个简单的问题,但我还没有在Stack Overflow上找到明确的答案。
struct foo { int x; int y; int z; }
foo Func()
{
return new foo();
}
void Func2()
{
foo f = Func(); // did boxing and unboxing occur?
}
从函数返回时,C#struct(值类型)总是总是复制到堆栈中,无论它有多大?我不确定的原因是,对于除MSIL之外的一些指令集(例如x86),返回值通常需要适合处理器寄存器,并且不直接涉及堆栈。
如果是这样,那么调用站点是否为CLR堆栈预先分配了(期望的)值返回类型的空间?
[编辑:回复摘要:]对于原始问题的意图,答案是否定的; CLR永远不会(默默地)将结构化为仅仅为了将其作为返回值发送。
答案 0 :(得分:7)
这是JIT编译器的重要实现细节。通常,如果结构足够小并且具有简单的成员,那么它将在CPU寄存器中返回。如果它变得太大,那么调用代码会在堆栈上保留足够的空间并将指针传递给该空间作为额外的隐藏参数。
它永远不会被装箱,除非方法的返回类型当然是 object 。
Fwiw:这也是调试器无法在Autos窗口中显示函数返回值的原因。有时痛苦。但是调试器没有从JIT编译器获得足够的元数据来确切地知道在哪里找到值。编辑:已在VS2013中修复。
答案 1 :(得分:6)
只要您想将结构视为object
,结构就会被装箱,因此如果您调用Func
并将结果分配给对象,则会将其装箱。
E.g。这样做
object o = Func();
将产生以下IL
L_0000: call valuetype TestApp.foo TestApp.Program::Func()
L_0005: box TestApp.foo
L_000a: stloc.0
表示返回值已装箱,因为我们将其分配给类型object
的引用。
如果将其分配给类型为Foo
的变量,则它不会被装箱,因此会被复制并将值存储在堆栈中。
此外,拳击在这里不会真正帮助你,因为它将涉及创建一个对象来表示结构的值,并且在装箱操作期间有效地复制值。