1

As Int32 is a struct which means it is a System.ValueType (which inherits System.Object), when I pass an Integer to a function which expects Object, why should CLR box it?

Does CLR assumes that Object is always a reference type?

It is a bit confusing to think that ValueType "is" an Object but when you have to pass it "as" object, you need box it...

Am I the only one who is wondering about this?

  • Many people have asked, but essentially ValueType is more like a flag than a normal type. It doesn't act the same way other types do. Neither does Object, really. Just accept that ValueType doesn't quite inherit the way you'd expect. – Magus Jan 24 '14 at 15:36
  • Because the function you pass it to would have to have 2 versions - one for heap objects, and one for stacked objects? The op codes would need to be different. – StuartLC Jan 24 '14 at 15:36
  • Do you actually understand what boxing is? – Damien_The_Unbeliever Jan 24 '14 at 15:39
  • possible duplicate of [Why do we need boxing and unboxing in C#?](http://stackoverflow.com/questions/2111857/why-do-we-need-boxing-and-unboxing-in-c) – Sriram Sakthivel Jan 24 '14 at 15:49
  • @SriramSakthivel: It's almost a duplicate, but this question more about the hierarchical nature of the type system, and why things which appear to be represented in said hierarchy are treated differently. – Magus Jan 24 '14 at 16:23
  • Damien_The_Unbeliever, I do understand what is boxing. My question was around "why" should it box if Integer is also "Object". When does compiler decides to box it.. and I think Dan has answered it brilliantly in his very first line. – ConfusedCoder Jan 25 '14 at 04:54

1 Answers1

1

It's not that a type derived from Object is always a reference type, but rather that a variable of type Object always contains a reference. Suppose you wanted to store the actual value in the Object; how then would you decide how big the Object value would need to be?

A variable of a compile-time-known value type has a known size for which space can be allocated, but an Object, being able to 'contain' any value type, cannot be sized in advance. One logical solution then is to have the Object variable contain a special type of reference to a boxed object, whereby the size of the 'box' is allocated dynamically depending on what type is being boxed.

Some slightly more technical notes:

Another solution to the above problem would be to treat the Object as a reference to an arbitrary location in memory, which would prevent having to create a boxed copy. This is how it's done in C, where you can create a pointer to a value on the stack, for instance, then pass that to another function for use. This can be quite dangerous though, as what happens, for instance, if the function decides to keep that pointer around and use it at some undefined later time. Since the call stack has changed, that pointer is now pointing to something entirely different than was originally intended and writing to it will almost certainly have disastrous side effects.

Part of the goal of .NET, as a managed runtime, is to provide a 'safe' environment where these particular kinds of failures can't happen. Part of that trade-off is disallowing persisted direct references to stack memory, necessitating boxing when you want to 'persist' the contents of a value type in a variable containing a reference. This used to be a performance problem with collections in .NET 1.1, but the addition of Generics in .NET 2.0 meant that boxing was far less common an occurrence.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Allowing `Object` to point anywhere in memory wouldn't solve anything; what would minimize boxing would be having an "anything" type that's large enough to hold at least a few bits beyond the number necessary to identify an object. When using a 64-bit `Object` type, for example, one could in theory specify that if four bits are all set, the remaining 60 bits identify an object. Otherwise, depending upon what those four bits hold, the object would represent a `double` in the range +/-1.34078E+154 [there are 3*2^62 such values], a `long` in the range +/- 2^59, etc. – supercat Feb 07 '14 at 21:30
  • In practice, trying to share bits that way would so massively increase the cost of dealing with the object that it would be better to use an extra field. Unless one wants to complicate the GC, however, one would probably have to have the object reference be entirely separate from the value part, perhaps having the `Anything` hold an object reference plus 8 or 16 bytes beyond [depending upon whether one wants to allow `Decimal` and `GUID` to avoid boxing]. When storing a value, the `Object` would identify a static object saying how to interpret the 16 byte value field. – supercat Feb 07 '14 at 21:33