C# 结构体在声明为函数的返回值时是否会被装箱?

发布于 2024-09-24 04:03:10 字数 687 浏览 8 评论 0原文

一个简单的问题,但我在 Stack Overflow 上没有找到明确的答案。

    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 堆栈上为(预期的)值返回类型预分配了空间?


[edit: Summary of replies]
For the intent of the original question, the answer is no; the CLR will never (silently) box a struct just for the purpose of transferring it as a return value.

A simple question, but I haven't found a definitive answer on Stack Overflow.

    struct MyStruct { int x, y, z; }

    MyStruct GetMyStruct() => new MyStruct();

    static void Main()
    {
        var x = GetMyStruct();      // can boxing/unboxing ever occur?
    }

Is a C# struct (value type) always copied to the stack when returned from a function, no matter how large it might be? The reason I'm unsure is that for some instruction sets other than MSIL (such as x86), a return value usually needs to fit into a processor register, and the stack is not directly involved.

If so, is it the call site that pre-allocates space on the CLR stack for the (expected) value return type?


[edit: Summary of replies]
For the intent of the original question, the answer is no; the CLR will never (silently) box a struct just for the purpose of transferring it as a return value.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱格式化 2024-10-01 04:03:10

这是 JIT 编译器的重要实现细节。一般来说,如果结构足够小并且具有简单的成员,那么它会在 CPU 寄存器中返回。如果它变得太大,则调用代码在堆栈上保留足够的空间,并将指向该空间的指针作为额外的隐藏参数传递。

当然,它永远不会被装箱,除非方法的返回类型是object

Fwiw:这也是调试器无法在 Autos 窗口中显示函数返回值的原因。有时很痛苦。但调试器无法从 JIT 编译器获取足够的元数据来准确了解在哪里可以找到该值。编辑:在 VS2013 中修复。

It is a heavy implementation detail of the JIT compiler. In general, if the struct is small enough and has simple members then its gets returned in CPU registers. If it gets too big then the calling code reserves enough space on the stack and passes a pointer to that space as an extra hidden argument.

It will never be boxed, unless the return type of the method is object of course.

Fwiw: this is also the reason that the debugger cannot display the return value of the function in the Autos window. Painful sometimes. But the debugger doesn't get enough metadata from the JIT compiler to know exactly where to find the value. Edit: fixed in VS2013.

撩人痒 2024-10-01 04:03:10

每当您想将结构体视为对象时,它就会被装箱,因此,如果您调用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 类型的变量,则它不会被装箱,因此会被复制并将值存储在堆栈中。

此外,装箱在这里并不能真正帮助您,因为它涉及创建一个对象来表示结构的值,并且这些值在装箱操作期间被有效复制。

A struct is boxed whenever you want to treat it as an object, so if you call Func and assign the result to object it will be boxed.

E.g. doing this

 object o = Func();

will yield the following IL

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

which shows that the return value is boxed, because we assign it to a reference of the type object.

If you assign it to a variable of type Foo it isn't boxed and thus it is copied and the value is stored on the stack.

Also, boxing wouldn't really help you here since it would involve creating an object to represent the value of the struct, and the values are effectively copied during the boxing operation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文