Why boxing reference types?
Asked Answered
K

2

6

ECMA-335, 1.8.2.4, specifies that boxable types include reference types (excluding managed pointers/byrefs) and generic parameters.

What is the purpose of boxing reference types? Is the functionality and memory representation of a boxed reference object any different compare to the unboxed one?

Knut answered 23/10, 2013 at 18:8 Comment(4)
.NET allows a reference type to be boxed, but C# does not. The runtime allows it, but that functionality will never be leveraged by the C# compiler.Hae
@Servy, how does that work in practice, though hypothetical I realize. Would it be like var o = (object)myRefInstance;? It's weird because myRefInstance clearly already bases object.Literally
Also, "If typeTok is a reference type, the box instruction does returns val unchanged as obj" - III.4.1.Eulogy
@neoistheone That's valid C# code that doesn't involve boxing. Note that it talks about boxing the actual reference type, not the reference pointing to it, so such a theoretical operation would actually perform a bitwise copy of the members of the instance of the object, not just copying the reference, as the code you showed would do. It would act similar to how MemberwiseClone acts.Hae
A
5

There's nothing logically wrong with boxing a reference type reference. It is just a no-op, nothing changes.

But Ecma-335 isn't always a good description for what is really implemented in the .NET CLR. The JIT_Box() helper function that implements Opcodes.Box will actually throw an InvalidCastException when it is asked to box a value that's not a value type. It expects a compiler and the jitter to know when to suppress the boxing conversion when it is unnecessary. They do.

Andromeda answered 23/10, 2013 at 18:50 Comment(6)
But it does accept a generic parameter that represents a reference type without throwing an exception. In that case the no-op behavior is required to support casting from T to object.Rapacious
That's a valid point. The jitter is already aware of T being a reference type and completely omits the code for the boxing conversion.Andromeda
Did you check if it actually throws when using box on a normal reference type? Perhaps the JITter optimizes it out in that case as well before it reaches the exception throw code.Rapacious
Yes, the jitter simply omits the call to JIT_Box for T's of a reference type. The reference is already good as-is.Andromeda
I meant the non generic case, since if it omitted the JIT_Box call in that case as well, the behaviour of the CLR would be consistent with the spec, and the InvalidCastException check would never trigger.Rapacious
I don't know how to write code that tests that, other than writing IL. Seems pointless, I know it throws from the source code.Andromeda
R
3

Consider the generic function:

object MyBox<T>(T value)
{
    return (object)value;
}

This compiles to:

ldarg.1     
box         01 00 00 1B 
ret

The expected behavior of this function is a no-op if T is a reference type, boxing the value to itself.

Boxing a value that's know to be a reference type is less useful, but specifying it in a way that's consistent with generics is simple and consistent.

Rapacious answered 23/10, 2013 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.