Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I unbox an int as a decimal?

I have an IDataRecord reader that I'm retrieving a decimal from as follows:

decimal d = (decimal)reader[0];

For some reason this throws an invalid cast exception saying that the "Specified cast is not valid."

When I do reader[0].GetType() it tells me that it is an Int32. As far as I know, this shouldn't be a problem....

I've tested this out by this snippet which works just fine.

int i = 3750;
decimal d = (decimal)i;

This has left me scratching my head wondering why it is failing to unbox the int contained in the reader as a decimal.

Does anyone know why this might be occurring? Is there something subtle I'm missing?

like image 812
mezoid Avatar asked Jul 06 '09 01:07

mezoid


4 Answers

You can only unbox a value type to its original type (and the nullable version of that type).

By the way, this is valid (just a shorthand for your two line version):

object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it's a widening conversion

For the reason behind this read this Eric Lippert's blog entry: Representation and Identity

Personally, I categorize things done by cast syntax into four different types of operation (they all have different IL instructions):

  1. Boxing (box IL instruction) and unboxing (unbox IL instruction)
  2. Casting through the inhertiance hierarchy (like dynamic_cast<Type> in C++, uses castclass IL instruction to verify)
  3. Casting between primitive types (like static_cast<Type> in C++, there are plenty of IL instructions for different types of casts between primitive types)
  4. Calling user defined conversion operators (at the IL level they are just method calls to the appropriate op_XXX method).
like image 51
mmx Avatar answered Nov 19 '22 08:11

mmx


There is no problem in casting an int to decimal, but when you are unboxing an object you have to use the exact type that the object contains.

To unbox the int value into a decimal value, you first unbox it as an int, then cast it to decimal:

decimal d = (decimal)(int)reader[0];

The IDataRecord interface also has methods for unboxing the value:

decimal d = (decimal)reader.GetInt32(0);
like image 42
Guffa Avatar answered Nov 19 '22 09:11

Guffa


Here is a simple solution. It takes care of unboxing and then casting to decimal. Worked fine for me.

decimal d = Convert.ToDecimal(reader[0]);  // reader[0] is int
like image 15
Sagar Avatar answered Nov 19 '22 10:11

Sagar


Mehrdad Afshari said it:

You can only unbox a value type to its original type (and the nullable version of that type).

The thing to realize is that there is a difference between casting and unboxing. jerryjvl had an excellent remark

In a sense it's a shame that unboxing and casting syntactically look identical, since they are very different operations.

Casting:

int i = 3750; // Declares a normal int
decimal d = (decimal)i; // Casts an int into a decimal > OK

Boxing/Unboxing:

object i = 3750; // Boxes an int ("3750" is similar to "(int)3750")
decimal d = (decimal)i; // Unboxes the boxed int into a decimal > KO, can only unbox it into a int or int?
like image 3
user276648 Avatar answered Nov 19 '22 08:11

user276648