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?
Solution 1:
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):
- Boxing (
box
IL instruction) and unboxing (unbox
IL instruction) - Casting through the inhertiance hierarchy (like
dynamic_cast<Type>
in C++, usescastclass
IL instruction to verify) - Casting between primitive types (like
static_cast<Type>
in C++, there are plenty of IL instructions for different types of casts between primitive types) - Calling user defined conversion operators (at the IL level they are just method calls to the appropriate
op_XXX
method).
Solution 2:
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);
Solution 3:
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
Solution 4:
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?