Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do structs need to be boxed?

Tags:

In C#, any user-defined struct is automatically a subclass of System.Struct System.ValueType and System.Struct System.ValueType is a subclass of System.Object.

But when we assign some struct to object-type reference it gets boxed. For example:

struct A {     public int i; }  A a; object obj = a;  // boxing takes place here 

So my question is: if A is an descendant of System.Object, can't the compiler up-cast it to object type instead of boxing?

like image 417
Red Hyena Avatar asked Dec 30 '09 05:12

Red Hyena


People also ask

Why struct is stored on the stack?

Most importantly, a struct unlike a class, is a value type. So, while instances of a class are stored in the heap, instances of a struct are stored in the stack. When an instance of a struct is passed to a method, it is always passed by value.

Why do we need structs?

1) Structures provide better performance when we have small collections of value-types that you want to group together. 2) Use Structure if all member fields are of value type. Use Class if any one member is of reference type.

Are structs more efficient than classes?

The only difference between these two methods is that the one allocates classes, and the other allocates structs. MeasureTestC allocates structs and runs in only 17 milliseconds which is 8.6 times faster than MeasureTestB which allocates classes! That's quite a difference!


2 Answers

A struct is a value type. System.Object is a reference type. Value types and reference types are stored and treated differently by the runtime. For a value type to be treated as a reference type, it's necessary for it to be boxed. From a low level perspective, this includes copying the value from the stack where it originally lives to the newly allocated memory on the heap, which also contains an object header. Additional headers are necessary for reference types to resolve their vtables to enable virtual method dispatches and other reference type related features (remember that a struct on stack is just a value and it has zero type information; it doesn't contain anything like vtables and can't be directly used to resolve dynamically dispatched methods). Besides, to treat something as a reference type, you have to have a reference (pointer) to it, not the raw value of it.

So my question is - if A is an descendant of System.Object, can't compiler upcast it to object type instead of boxing?

At a lower level, a value does not inherit anything. Actually, as I said before, it's not really an object. The fact that A derives from System.ValueType which in turn derives from System.Object is something defined at the abstraction level of your programming language (C#) and C# is indeed hiding the boxing operation from you pretty well. You don't mention anything explicitly to box the value so you can simply think the compiler has "upcasted" the structure for you. It's making the illusion of inheritance and polymorphism for values while none of the tools required for polymorphic behavior is directly provided by them.

like image 76
mmx Avatar answered Sep 30 '22 16:09

mmx


Here's how I prefer to think about it. Consider the implementation of a variable containing a 32 bit integer. When treated as a value type, the entire value fits into 32 bits of storage. That's what a value type is: the storage contains just the bits that make up the value, nothing more, nothing less.

Now consider the implementation of a variable containing an object reference. The variable contains a "reference", which could be implemented in any number of ways. It could be a handle into a garbage collector structure, or it could be an address on the managed heap, or whatever. But it's something which allows you to find an object. That's what a reference type is: the storage associated with a variable of reference type contains some bits that allow you to reference an object.

Clearly those two things are completely different.

Now suppose you have a variable of type object, and you wish to copy the contents of a variable of type int into it. How do you do it? The 32 bits that make up an integer aren't one of these "reference" things, it's just a bucket that contains 32 bits. References could be 64 bit pointers into the managed heap, or 32 bit handles into a garbage collector data structure, or any other implementation you can think of, but a 32 bit integer can only be a 32 bit integer.

So what you do in that scenario is you box the integer: you make a new object that contains storage for an integer, and then you store a reference to the new object.

Boxing is only necessary if you want to (1) have a unified type system, and (2) ensure that a 32 bit integer consumes 32 bits of memory. If you're willing to reject either of those then you don't need boxing; we are not willing to reject those, and so boxing is what we're forced to live with.

like image 44
Eric Lippert Avatar answered Sep 30 '22 16:09

Eric Lippert