0

In C#, why does Example 1 work:

int myValue1 = 11;
double resultDirectlyFromInt = myValue1;

But Example 2 does not:

int myValue2 = 22;
object myObject2 = myValue2;
double resultFromBoxedInt = (double)myObject2;

Yet again Example 3 does work:

double myValue3 = 33.3;
object myObject3 = myValue3;
double resultFromBoxedDouble = (double)myObject3;

Can someone explain the rationale behind that ? Because to me, working examples 1 and 3 look like proofs that example 2 should work.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Gilles jr Bisson
  • 449
  • 4
  • 11
  • You can use Convert.ToDouble(myObject2). The c# language just doesn't support casting from int to double. c# does support setting an int to a double without casting. It is just a quirk in the language. – jdweng Jul 27 '23 at 15:58
  • 1
    "The c# language just doesn't support casting from int to double." Yes it does, as both an implicit or explicit conversion. It doesn't support *unboxing* a boxed int to double. – Jon Skeet Jul 27 '23 at 17:27

2 Answers2

5

Because to me, working examples 1 and 3 look like proofs that example 2 should work.

No, they're very different conversions.

C# defines a conversion from int to double (see section 10.2.3 of the C# 7 draft standard). But unboxing conversions are defined very differently, in section 10.3.7:

An unboxing operation to a non_nullable_value_type consists of first checking that the object instance is a boxed value of the given non_nullable_value_type, and then copying the value out of the instance.

...

If the source operand is a reference to an incompatible object, a System.InvalidCastException is thrown.

In your example 2, the object instance is not a boxed value of type double - it's a boxed value of type int, so the check described above fails.

As it happens, the .NET CLR is a little more forgiving in terms of unboxing than C# requires; you can unbox from a boxed int to a uint or vice versa, and also from an enum to its underlying type or vice versa. But it doesn't permit anything that requires a representation change, like int to double.

If you want a justification for those rules, think of it this way: in order to perform any conversion that performs real work (as opposed to just copying a bit pattern from one place to another), the compiler needs to tell the CLR which conversion to perform. For an unboxing operation, the actual type is only known at execution time, so the compiler can't instruct the CLR what to do. The CLR could have more complex rules so that it could perform the conversion based on the execution-time type - but that's straying into territory that is usually language-based.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Feel bad for not up-voting your answer :)... While I like (and agree with) the justification part (which is what OP seem to be asking for) I don't think it's authoritative enough. I don't know if such explanation actually exists somewhere so... – Alexei Levenkov Jul 27 '23 at 16:58
  • Thanks @Jon Skeet. I get the concept now. – Gilles jr Bisson Jul 29 '23 at 20:21
  • @AlexeiLevenkov While the links to thedocs are valuable, I fell your edits changed the tone of my question which was not necessary. Perhaps the links and implied comments would have been better in an answer. – Gilles jr Bisson Jul 29 '23 at 20:25
  • @GillesjrBisson sorry. Will keep to my "downvote and move on" next time. Thanks for reminding. – Alexei Levenkov Jul 30 '23 at 03:51
0

Your first example is actually calling the operator called implicit conversion operator, which you can find more info in this link, and this happen to be implemented in compiler level. The reason why it works is compiler knows that Int32 integers can be represent fully in a Double-type variable during compile time.

In your example 2, the reason why it does not work is it during unbox, you must cast it back to the original type first, so that the runtime can ensure the type safety. Reference to other answers as well. Instead, you can change the example 2 as below so that it will run without runtime complaining, which is kind of explaining how example 1 works under-the-hood with the cast to double, but explicitly.

int myValue2 = 22;
object myObject2 = myValue2;
double resultFromBoxedInt = (double)(int)myObject2;
WWW
  • 63
  • 5
  • The reason I can't do that in my project is that the boxed value I am trying to cast to double may be originally either an int or a double. So I guess Convert.ToDouble() is my way to go. – Gilles jr Bisson Jul 29 '23 at 20:13