Strange casting behaviour. Cannot cast object (int) to long

C#.NetClr

C# Problem Overview


I have the following code:

int intNumber1 = 100;
object intNumber2 = 100;
bool areNumberOfTheSameType = intNumber1.GetType() == intNumber2.GetType(); // TRUE
bool areEqual = intNumber1.Equals(intNumber2); // TRUE
        
long longNumber1 = (long) intNumber1; // OK
long longNumber2 = (long) intNumber2; // InvalidCastException. Why?

Why doesn't the second cast work? I realize that it might be because the object doesn’t have an explicit cast to a long, but if we look at its type on runtime it is System.Int32.

If I use var or dynamic instead of object, it works.

Any thoughts?

C# Solutions


Solution 1 - C#

Cast from int to long is interpreted as conversion between the two types.

Cast from object to int is interpreted as unboxing a boxed int.

It is the same syntax, but it says two different things.

In the working cases (intlong, object (boxed int)→int), the compiler knows exactly what code to produce. If boxed intlong was to work, the compiler would have to somehow figure out which conversion to use, but it doesn't have enough information to do it.

See also this blog post from Eric Lippert.

Solution 2 - C#

The object holds a type int. But it's considered an object (which is a boxed int) and a boxed value type can generally only be cast to its underlying type (the type that is boxed).

To cast it to another type, you first have to cast it to its underlying type. This works:

long longNumber2 = (long) (int) intNumber2;

The reason that var works is that the compiler infers the type at compile time. That means, when you use var, the type of intNumber2 (if you use typeof) will be int. Whereas when you use object, the type will be object.

Using dynamic is a whole different process and cannot be compared with var. Here, the conversion / casting takes place at runtime, using reflection and the DLR library. It will dynamically find the underlying type, find that it has a conversion operator and uses that.

Solution 3 - C#

That it doesn't work due to being two different types of casts (one converting, the other unboxing) has already been stated in answers here. What might be a useful addition, is that Convert.ToInt64() will convert anything that is either a built-in type that can be converted to long, or a type of a class that implements IConvertible.ToInt64(), into a long. In other words, if you want to be able to cast an object that contains an integer (of whatever size) to long, Convert.ToInt64() is the way to go. It is more expensive, but what you are trying to do is more expensive that casting, and the difference is negliable (just big enough to be wasteful in cases where you know the object must be a boxed long).

Solution 4 - C#

(Caution: Guess)

Int32 has a conversion operator to Int64 which is what gets invoked when you do the first cast. Object doesn't, so your second cast is trying to cast an object to another type which isn't a supertype (Int64 doesn't inherit Int32).

The reason why it works with var is obvious – the compiler just saves you from typing int in that case. With dynamic the runtime does all necessary checks for what needs to be done while normally the compiler would just insert either the cast or invoke the conversion operator.

Solution 5 - C#

You need to unbox to the same type that was boxed.

object intNumber2 = 100L;
// or value in the long type range
// object intNumber2 = 9223372036854775806;

long result = (long)intNumber2;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBashir MagomedovView Question on Stackoverflow
Solution 1 - C#svickView Answer on Stackoverflow
Solution 2 - C#AbelView Answer on Stackoverflow
Solution 3 - C#Jon HannaView Answer on Stackoverflow
Solution 4 - C#JoeyView Answer on Stackoverflow
Solution 5 - C#Peter KellyView Answer on Stackoverflow