20

Boxing is the process of converting a value type into a managed heap object, which is implicit. Unboxing is the reverse process, for which the compiler requires an explicit cast. Since boxing stores the data type, why can't unboxing use that instead of asking for an explicit cast?

class BoxUnBox
{
 static void Main()
 {
   int i = 123;      // a value type
   object o = i;     // boxing
   int j = (int)o;   // unboxing - Why is an explicit cast required?
 }
}
Boann
  • 48,794
  • 16
  • 117
  • 146
RJN
  • 696
  • 8
  • 17
  • 21
    Maybe because its unsafe? You can have anything stored in `object` – Sergey Berezovskiy Feb 22 '17 at 11:49
  • how about "o as int"? i know its still explicit, but its often prettier than a cast – downrep_nation Feb 22 '17 at 11:57
  • 1
    @downrep_nation `o as int` doesn´t work because `int` is a value-type which can´t be used in conjunction with an `as`-cast. You may however use `o as int?`. – MakePeaceGreatAgain Feb 22 '17 at 12:02
  • 1
    @HimBromBeere good catch, always great learning something new. only now the different syntax is explained to me. i love this community. – downrep_nation Feb 22 '17 at 12:03
  • 1
    It doesn't have anything to do with unboxing, a downcast always requires an explicit cast. It is risky so you have to let the compiler know what you are doing is intended. Just declare the `i` variable as `long` to see it blowing up. Now do it correctly, `int j = Convert.ToInt32(o);` – Hans Passant Feb 22 '17 at 12:09
  • 7
    Although the boxed type is known at run-time, it is unknown at compile-time. So you have to tell the compiler what you expect to pop out of the box. – Emond Feb 22 '17 at 12:55
  • 4
    Your next question is highly likely to be "OK, why does the unboxing cast have to be *the exact type of the boxed object*?" That is, if I can cast an int to long, why can't I unbox a boxed int by casting to long? I encourage you to *think about that question for a while*, and then read https://ericlippert.com/2009/03/03/representation-and-identity/ – Eric Lippert Feb 23 '17 at 00:09
  • @EricLippert: Eh, I don't buy it. C# could've easily supported this for native types without any performance overhead... e.g. on x86 you just need to store the boxed value as `long long` and then, when it's time to unbox, you just copy however many bytes you need to into the variable. And type checking could've just amounted to `typecode1 <= typecode2` if you'd designed it right. – user541686 Feb 23 '17 at 05:00

7 Answers7

42

Your question is not related to unboxing operation only. Actually it should sound as "Why should I use explicit conversion?" Consider following example:

int i = 123;
long l = i;
int j = (int)l; // OMG why??

The answer is simple and you can find it in C# specification 6.2 Explicit conversions:

The explicit conversions are conversions that cannot be proven to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit explicit notation.

In example above you might lose information, because long can hold values which do not fit int range. But you will never lose information when assigning int to long:

long l = i; // safe

In your example you need explicit conversion because implicit conversion cannot be proven to always succeed. Variables of object type can reference literally any type. What about string?

object o = i;  // implicit and always safe
o = "Now I have a machinegun ho-ho-ho"; // safe too
int j = o;     // will not succeed if o is string

Analogy

Object variable is like a black box where you can put anything - music CD, pen, phone or banana. Not only you, but anyone can put something there. If the last thing which you put in a black box in the morning was a banana, can you come back in the evening and eat whatever you pull out from the black box? If you live alone, and room is closed, and your memory is excellent, and... then you can. And you will wonder why everybody checks their box's content before eating it. But if you are not live alone, or the room is not closed, or you can forget just once that you have put the phone into the box... Bon appetite

Rob
  • 26,989
  • 16
  • 82
  • 98
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • The conversion from `long` to `int` meets the criteria for explicitness in two ways: in an unchecked context, you possibly lose information; in a checked context, the conversion can fail on an `OverflowException`. (Programmers often seem to forget C# has checked arithmetic, and indeed it's not the default.) – Jeroen Mostert Feb 22 '17 at 12:43
  • 1
    Very nice analogy! A nice way to explain the idea – Prokurors Feb 23 '17 at 11:08
6

What if someone changes the content of o to say "Hello World". To be sure you know what you're doing, the compiler requires you to explicitly cast the boxed value.

Basically an implicit conversion implies that any instance o, of type object, can also be represented as an instance of int which clearly isn't the case. Consider for example this:

int i = -1;
long j = i;

It is clear that your variable i, which is an integer, can also be considered as long. This is why implicit casting is accurate here. On the other side, not every long is also convertable to int without any loss of data. Thus you need an explicit cast to determine: I know there might be some loss of data, however I don't care about it.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
5

Because an Int32 is an Object, but an Object may be an Int32. The compiler knows what to do in the first case, but you have to tell the compiler you know what you're doing in the second case, and guarantee that the unboxing can be performed.

The inheritance relationship is directional! Parent is different from child.

Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53
  • no, `Int32` is a value-type and thus not an `object`. However you can *convert* it to `object*, which is what boxing means. – MakePeaceGreatAgain Feb 22 '17 at 11:55
  • 5
    This is C#, not Java. EVERYTHING inherits from object, even primitive types. See http://stackoverflow.com/a/33247833/1395758. – Alberto Chiesa Feb 22 '17 at 11:56
  • @him "`Int32` is an alias for `int`": technically false: `int` in C# is an alias for `System.Int32`, that is a .NET "special" `struct`... The rest of the sentence is ok. – xanatos Feb 22 '17 at 11:56
  • `Int32` is an alias for `int` and thus not an `object` is strictly wrong. – Alberto Chiesa Feb 22 '17 at 11:57
  • 2
    @A.Chiesa It is right and wrong... Why technically everything inherits from object, for value types the inheritance is "strange" – xanatos Feb 22 '17 at 11:57
  • @xanatos: you're right. The implementation has some caveats, being highly optimized for performance. From a programmers perspective, it's usually more useful to think about Int32 being a struct, and struct being a class with some memory management differences. This sentence strictly IMO. – Alberto Chiesa Feb 22 '17 at 11:59
  • @xanatos Technically *everything* doesn't inherit from `object`. Pointers don't inherit from `object`, nor does `Void`. – Servy Feb 22 '17 at 14:31
  • @Servy: if you want to get *that* technical -- `Void` is just an artifact of `System.Reflection`. It is a "privileged struct" in much the same way `Int32` is. Methods/functions that return nothing do not, in fact, return `Void`. `Void` inherits from `ValueType`, which in turn inherits from, of course, `Object`. – Jeroen Mostert Feb 22 '17 at 16:05
  • @JeroenMostert `Void` is a type because the specs define it as a type. `Void` is not an `object` because the specs also say as much. The details of the actual implementation surrounding it *can't* change that though. Those statements are true because they're *defined* to be true. – Servy Feb 22 '17 at 16:17
  • @Servy - are you sure void is defined as a type? I can't find this definition, when searching in the specification. I'm just curious ;) – Alberto Chiesa Feb 22 '17 at 16:34
  • @Servy: having combed both the C# and CLI specs I'm pretty sure the issue is more subtle than you state, but also moot, and *certainly* too far off topic to continue discussing. It's much ado about `Void` (and `void`, which is almost but not quite the same thing.) The case for pointers is far more straightforward (and has practical consequences). – Jeroen Mostert Feb 22 '17 at 16:37
  • @A.Chiesa: the C# spec defines `typeof(void)` as `System.Void` (the latter is defined in the CLI spec as well), and states "a method's return type is `void` if it does not return a value", thereby implicitly making it a type (or at least a *return type*). The CLI spec is more subtle on the matter, but in C#, `void` is a type. Ish. (It's still true that methods that are declared as returning `void` do not return a value, and in particular, they do not return instances of `Void`, as no value can ever be of type `Void`.) [See also.](http://stackoverflow.com/q/5450748) – Jeroen Mostert Feb 22 '17 at 16:54
4

Any int is convertible to an object. Not all objects can be cast to ints.

Connell.O'Donnell
  • 3,603
  • 11
  • 27
  • 61
3

The compiler cannot guarantee what is inside of your object. That is why you need to explicitly cast as the value you expect. For the compiler:

This is as hazardous

object o = 45;
int j = (int)o;

as this:

object o = "something";
int j = (int)o;

And that cannot be allowed at compile time.

Winter
  • 3,894
  • 7
  • 24
  • 56
NicoRiff
  • 4,803
  • 3
  • 25
  • 54
2

Casting might fail at runtime, depending on what your object really contains. When implicit unboxing would be possible, you might overlook errors as you may have written something which was meant differently (either by you or you misunderstood someone else's code). The compiler requires you to cast explicitly because you should really want that cast. Otherwise you might mistakenly mix types, which produces verry error prone code. By beeing forced to cast explicitly you are forced to think twice if what you do is right.

Georg Jung
  • 949
  • 10
  • 27
1

Use of Dynamic avoids the cast

Not to take away from anything that has been said, but I wanted to point out that, technically, the cast is not always needed to perform the unboxing. The dynamic keyword allows for the system to perform the unboxing and conversion automagically. I am neither recommending nor discouraging the use of dynamic, merely pointing out it's behavior.

static void DynamicTest()
{
    int i = 123;      // a value type
    object o = i;     // boxing
    dynamic d = o;    // shift to dynamic
    int j = d;        // unboxing - No cast required
}

Edit: Jeroen Mostert wisely points out that the dynamic keyword is not any kind of magic that always makes this work. It merely defers evaluation to a runtime behavior. So, while the above example will always work, more complex examples will definitely fail. Therefore, one must take care when using the dynamic keyword and expect (try/catch) runtime failures. The dynamic keyword can, none-the-less be a very powerful tool, if used judiciously.

Reginald Blue
  • 930
  • 11
  • 26
  • 2
    You should mention that `dynamic` always defers looking up the appropriate conversion to runtime. That is its behavior; it's not a magic conversion system. (In particular, if in the snippet above `long i = 123` is used, the code will compile fine but fail at runtime because there's no implicit conversion from `long` to `int`.) – Jeroen Mostert Feb 22 '17 at 17:11