当声明为函数的返回值时,C#struct是否曾经装箱?
一个简单的问题,但我还没有找到关于堆栈溢出的确切答案当声明为函数的返回值时,C#struct是否曾经装箱?,c#,struct,return-value,boxing,value-type,C#,Struct,Return Value,Boxing,Value Type,一个简单的问题,但我还没有找到关于堆栈溢出的确切答案 struct MyStruct { int x, y, z; } MyStruct GetMyStruct() => new MyStruct(); static void Main() { var x = GetMyStruct(); // can boxing/unboxing ever occur? } 当从函数返回时,C#结构(值类型)是否总是复制到堆栈中,
struct MyStruct { int x, y, z; }
MyStruct GetMyStruct() => new MyStruct();
static void Main()
{
var x = GetMyStruct(); // can boxing/unboxing ever occur?
}
当从函数返回时,C#结构(值类型)是否总是复制到堆栈中,无论它有多大?我不确定的原因是,对于MSIL以外的某些指令集(如x86),通常需要将返回值放入处理器寄存器,而堆栈并不直接涉及
如果是,是调用站点在CLR堆栈上为(预期)值返回类型预先分配了空间吗
[编辑:答复摘要:]对于原始问题的意图,答案是否定的;CLR永远不会(无提示地)为将结构作为返回值发送而装箱。每当您想将结构作为
对象处理时,它都会装箱,因此如果调用Func
并将结果分配给对象,它就会装箱
例如,这样做
object o = Func();
将产生以下IL
L_0000: call valuetype TestApp.foo TestApp.Program::Func()
L_0005: box TestApp.foo
L_000a: stloc.0
这表明返回值已装箱,因为我们将其分配给类型为对象
的引用
如果将其分配给类型为Foo
的变量,则不会将其装箱,因此会复制该变量并将值存储在堆栈上
此外,装箱在这里并没有真正的帮助,因为它需要创建一个对象来表示结构的值,并且在装箱操作期间会有效地复制这些值 这是JIT编译器的一个繁重的实现细节。通常,如果结构足够小并且具有简单的成员,那么它将在CPU寄存器中返回。如果它变得太大,则调用代码在堆栈上保留足够的空间,并将指向该空间的指针作为额外的隐藏参数传递
它永远不会被装箱,除非方法的返回类型是object
Fwiw:这也是调试器无法在Autos窗口中显示函数返回值的原因。有时很痛苦。但是调试器没有从JIT编译器中获得足够的元数据来准确地知道在哪里找到值。编辑:在VS2013中修复。调用站点必须在堆栈上分配适当数量的字节这一事实是.NET设计指南建议struct
s在理想情况下不应大于16字节的原因。@Richard:您有该信息的来源吗?是的,其中包含16字节的建议,但并没有说这是因为呼叫站点必须分配数据。我会努力追踪我读到这篇文章的具体来源。@JimMischel根据答案,如果你想保证并确保避免BLT大型结构,您应该显式地使用ref
或out
而不是返回结构。如果值类型的返回是ever(您使用的“ever”似乎意味着有时如果发生,它不是无条件的)装箱,这意味着当我们将返回赋给值类型变量时,会隐式取消装箱,这似乎效率低下。我对MSIL和CLR不太了解,所以我在这里帮不了你。不管怎样,我可以知道是什么引起了你的问题吗?@Bliz面食:当我意识到我错误地认为“ref”(和“out”)关键字导致了值类型的装箱(事实并非如此)时,我原来的问题实际上变得毫无意义。为了避免这种情况,我打算“返回”结构。但是,一旦我记住“ref”并没有框选值类型,使用out参数就成了一个选项。我的值类型的大小目前是4+IntPtr.size字节,并且数十万个这样的结构被非常密集地操作。