C# - Issues with boxing / unboxing / typecasting ints. I don't understand
Asked Answered
B

3

8

I'm having a hard time understanding this. Consider the following example:

protected void Page_Load(object sender, EventArgs e)
{
    // No surprise that this works
    Int16 firstTest = Convert.ToInt16(0);
    int firstTest2 = (int)firstTest;

    // This also works
    object secondTest = 0;
    int secondTest2 = (int)secondTest;

    // But this fails!
    object thirdTest = Convert.ToInt16(0);
    int thirdtest2 = (int)thirdTest;  // It blows up on this line.
}

The specific error that I get at runtime is Specified cast is not valid. If I QuickWatch (int)thirdTest in Visual Studio, I get a value of Cannot unbox 'thirdTest' as a 'int'.

What the heck is going on here?

Brandi answered 7/1, 2012 at 17:12 Comment(1)
Int16 is actually a short. So I think you can replace 'Int16 firstTest = Convert.ToInt16(0);' with 'Int16 firstTest = 0s;'Martamartaban
F
12

Unboxing checks the exact type as explained in the documentation.

Unboxing is an explicit conversion from the type object to a value type or from an interface type to a value type that implements the interface. An unboxing operation consists of:

  • Checking the object instance to make sure that it is a boxed value of the given value type.

  • Copying the value from the instance into the value-type variable.

As you can see the first step is to check that the object instance matches the target type.

Also quote from the documentation:

For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null causes a NullReferenceException. Attempting to unbox a reference to an incompatible value type causes an InvalidCastException.

So to fix this error make sure that the type matches before attempting to unbox:

object thirdTest = Convert.ToInt16(0);
short thirdtest2 = (short)thirdTest;  
Feline answered 7/1, 2012 at 17:17 Comment(1)
Or unbox it with the correct type, then cast to your desired type. int thirdtest2 = (int)(short)thirdTest;Ferryman
A
9

What's going on is exactly what it says.

In the first case, you have a short, unboxed, that you are then explicitly typecasting to an int. This is a valid conversion that the compiler knows how to do, so it works.

In the second case, you have an int, boxed, that are are assigning back to an int. This is a simple unboxing of an integer, which also valid, so it works.

In the third case, you have a short, boxed, that are you trying to unbox into a variable that is not a short. This isn't a valid operation: you can't do this in one step. This is not an uncommon problem, either: if you are using, for example, a SqlDataReader that contains a SMALLINT column, you cannot do:

    int x = (int)rdr["SmallIntColumn"];

Either of the following should work in your third example:

    object thirdTest = Convert.ToInt16(0);
    int thirdTest2 = Convert.ToInt32(thirdTest);
    int thirdTest3 = (int)(short)thirdTest;
Appellate answered 7/1, 2012 at 17:18 Comment(0)
Y
4

Int16 is a fancy way to write short; there is no boxing/unboxing going on there, just the plain CLR conversion between 16-bit and 32-bit integers.

The second case boxes and unboxes to the same type, which is allowed: value type int gets wrapped in an object, and then gets unwrapped.

The third case tries to unbox to a different type (int instead of short) which is not allowed.

Yoong answered 7/1, 2012 at 17:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.