tl;dr: Boxing isn't "how you create a reference"; it's "how you package a primitive value type for consumers who don't expect that exact type".
In .NET, reference types are class instances on the heap. Value types like int
or double
are just the bytes: A 32-bit int is just four bytes worth of zeroes and ones. When you put it in, say a System.List
(the old-timey pre-generic kind, that Granpaw whittled out down at the General Store), then take it back out, how will the compiler know what to do if you call GetType() on it? It would just have four bytes of... what? Who knows? If it stored a pointer in the List
, it would have a pointer to four bytes of... who knows?
In your own method, the generated code knows what your variable is. Regular strong type-checking. But that doesn't work when you send your variable's value it to somebody else who only knows he's expecting Object
.
So when you add an int
to a List
, or pass it to a function that takes Object
as an argument, the compiler has to add some information to it so everybody else knows what he's getting.
So "Boxing" means packaging a non-reference value into an object that can be treated as an instance of Object
. For ordinary ref
parameters, that's not necessary, because the type is known the whole way: The code generated for the guts of the function doesn't have to be prepared to deal with any arbitrary reference type. It knows it's getting (for example) a pointer to an integer, and that's all it's going to get. Boxing provides capability that's not required in this case, and so the compiler doesn't waste your users' cycles on it.
Boxing isn't the only way to have a reference (in the broadest sense of the term) to, for example, a double
. Rather, boxing is the only way to treat a double
as an object that can be stored in a System.List
: It has to be on the heap, it has to be castable to Object
, has to have run-time type information, etc. etc.
For the following, all all the caller or the callee need is the address of 64 zeroes and ones somewhere:
void f(ref double d) { d *= 2; }