tags:

views:

302

answers:

2

Hi,

Why there is InvalidCastException thrown? Can someone describe me this behavior?

object zero = 0;
decimal? dec = (decimal?)zero;

TIA

+12  A: 

A boxed int can only be unboxed to an int. This, however, is legal:

object zero = 0;
decimal? dec = (decimal?)(int)zero;

See MSDN or the ECMA 334 C# spec for details. The key here is the following:

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:

  1. Checking the object instance to make sure that it is a boxed value of the given value type.
  2. Copying the value from the instance into the value-type variable.

Edit: This linked article is worth pulling out of the comments. Thanks Rob Kennedy!

Jason
Ok, but why such interim casting is necessary?
Przemaas
The first cast (int) unboxes the boxed int to a value type of type int (this is a legal operation). The second cast (decimal?) casts the resulting int to decimal?.
Jason
Microsoft (for good reason) never wrote a implicit cast overload for object to decimal.
Michael Meadows
@Michael Meadows: This is legal: object zero = 0m; decimal? dec = (decimal)zero; Note the 'm' explicitly telling the compiler that the literal "0" is to be interpreted as a decimal.
Jason
@Jason, a decimal is an object, so the first example is polymorphism. The second example is explicit casting. What I was saying was to answer @Przemaas' comment. You must explicitly cast the "zero" variable to int first, because you cannot implicitly cast an object to int, and since it's not an INullable<decimal>, the explicit cast directly to that type fails.
Michael Meadows
@Michael Meadows: Either I misread your comment, or you deleted it and changed "explicit" to "implicit." I initially read your comment to state that there was no explicit cast from object to decimal. Apologies if I misread.
Jason
Eric Lippert goes into quite some detail about why this works the way it does. The summary: Unboxing to anything but the original type would involve *a lot* of generated code for all the different possibilities since the compiler doesn't know what the original boxed type is. http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx
Rob Kennedy
@Rob : I was looking for this post but wasn't able to find it... thanks ;)
Thomas Levesque
A: 

See this article http://msdn.microsoft.com/en-us/magazine/cc301569.aspx

Specifically "The common language runtime first ensures that the reference type variable is not null and that it refers to an object that is a boxed value of the desired value type. If either test fails, then an InvalidCastException exception is generated."

I think you are failing on the object of that value. I think the coversion to int works because that 0 literal will convert to an int and then an int converts to decimal.

If you do this it works

 decimal? test=0;
 object zero = test;
 decimal? dec = (decimal?)zero;

But I think the "0" in your snippet is not a "decimal" type.

I am still not positive cause this gets the same exception.

        int test=0;
     object zero = test;
     decimal? dec = (decimal?)zero;
Maestro1024
My problem originally comes from below method:public TResult Convert(object value){try { return (TResult)value; }catch (InvalidCastException) { return default(TResult); }}This method returns null when called:Convert<decimal?>(0);I cann't make any assumptions about TResult and I don't know what exactly comes as an argument. That is why I cannot use Convert.To...() goodies.
Przemaas