views:

107

answers:

2

A simple question, but I haven't found a definitive answer on 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?
    }

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?

+4  A: 

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.

Brian Rasmussen
The fact that the call site must allocate the appropriate number of bytes on the stack is the reason why the .NET design guidelines suggest that `struct`s should ideally be no bigger than 16 bytes.
Richard Cook
@Richard: Do you have a source for that info?
Jim Mischel
Check out http://msdn.microsoft.com/en-us/library/y23b5415(VS.71).aspx
Richard Cook
Yes, that contains the 16 byte recommendation, but doesn't say it's because the call site has to allocate data.
Jim Mischel
I'll try to track down the specific source where I read this.
Richard Cook
+1  A: 

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.

Hans Passant