Casting in C#

S

Steven Bartley

Error Message on execution:
An unhandled exception of
type 'System.InvalidCastException' occurred in
CastObjectToDecimal.exe

Additional information: Specified cast is not valid.

The code in question is:
object x = 15;
decimal d = 0;
d = (decimal)x;

Any work arounds for this??
Thanks
Steven
 
J

Jon Skeet [C# MVP]

Steven Bartley said:
Error Message on execution:
An unhandled exception of
type 'System.InvalidCastException' occurred in
CastObjectToDecimal.exe

Additional information: Specified cast is not valid.

The code in question is:
object x = 15;
decimal d = 0;
d = (decimal)x;

Any work arounds for this??

Yes:

decimal d = (decimal) (int) x;

Basically you can't cast from object to a primitive type other than the
actual type of the boxed value. However, having cast to that original
primitive type, you can then use an explicit conversion to get from
that to the new type you're interested in.
 
S

Sam Sungshik Kong

You may want to read Eric Gunnerson's article about boxing.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp03152001.asp

<From the article>

Consider the following code:

int value = 123;
object o = value; // box int into an object box
int value2 = (int) o; // unbox into value2

When value is assigned to o, as part of the assignment the C# compiler
creates a reference-type box big enough to hold the int on the heap, copies
the value to that box, then marks the box with the actual type (System.Int32
in this case) so the runtime knows what type is in the box

To get a value out of a box, we must specify what type we think is in the
box (since object can hold any type) with a cast. During execution, the
runtime will check whether the type that the object variable refers to is
the type specified in the cast. If the type is correct, the value will be
copied from the box back to the value type variable. If the type is
incorrect, an exception will be thrown.

Note that no other conversions can happen as part of the unboxing operation;
the types must match exactly. In other words, if we write:

long value2 = (long) o; // boxed value is an int

and o is a boxed int, an exception will be thrown. We can, however, write:

long value2 = (long)(int) o;

and the conversion will work as expected.
 
M

Muni Bajpai

So the issue is that you are expilicitly downcasting illegally .. lets go
through this step by step

object x = 15; // Your asssumption is that this is stored as an Object with
an Implicit upcast to a Decimal ... WONT work
This is bad because later you assume that x HAS all the functionality
wrapped in a decimal object by casting to it. This is Illegal not only from
the perspective of OOP but the compiler also tells you that by throwing an
exception.

What are you really trying to achieve .. tell us and we might be able to
help.

You could work around the Exception by using

d = x as decimal; but all that does is that it will assign null to d without
throwing an exception.

Cheers

Muni
 
F

Frank Oquendo

Muni said:
d = x as decimal; but all that does is that it will assign null to d
without throwing an exception.

I thought you could only use the as operator with reference types.

--
There are 10 kinds of people. Those who understand binary and those who
don't.

http://code.acadx.com
(Pull the pin to reply)
 
M

Muni Bajpai

oops thought he was using 'Decimal' ... and thats correct you can only us
the 'as' operator with reference types
 
S

Steven Bartley

Thanks for the quick response. However::::::
This issue arose from creating a code generator for a
project. The generator relies on a set of helper classes,
one of which maintains an ArrayList of objects as follows.

public object this[int index]
{
get
{
return _data[index];
}
set
{
_data[index] = value;
}
}

The problem is exacerbated by the fact that some of the
data types are home-grown as is
CoreDataTypes.s4CommissionRate below.

public static Item Insert(string firstName,
string lastName, string email, System.Decimal rate,
CoreDataTypes.s4CommissionRate commRate)
{
return Item.DBHelper.Insert(firstName,
lastName, email, rate, commRate);
}

So the game plan was to remove objects from the _data
array and cast them into whatever was required. In the
case of commRate, the field is a decimal in the source
database, but is manipulated within the runtime
environment as a CoreDataTypes.s4CommissionRate. I do not
want the object containing the reference to
CoreDataTypes.s4CommissionRate to have to be concerned
with having to know what the actual DB storage type is.

I did discover that (CoreDataTypes.s4CommissionRate)
(decimal)commRate would work, but it defeats my
intentions in terms of hiding the database details from
the higher abstraction objects.

I am able to get around the problem by changing a few
things in the code generator, and I understand what is
happening with the boxing, but somehow, this seems to
really weaken one's ability to support tier oriented
architecture. Since I am new to C#, I was really hoping I
had overlooked something.

Thanks again for the quick response
Steven
 
R

Rob Teixeira [MVP]

Why does this weaken C#'s ability to support tier oriented architecture?
Invest in strong-typed collection instead of an arraylist of generic
object - you won't regret it.
This will also get better in the next release of VS as you can use generics
to create specific-typed collections without relying on generic object
references.
If your code generator can't work in this fashion, I'd chuck it out the
window. :)

-Rob Teixeira [MVP]
 
M

Marco Martin

Shouldn't this work?
object x = new object();
x = 16;

//The Convert.ToDecimal can accept an object as long as its IConvertable...

decimal dec = Convert.ToDecimal(x);

MessageBox.Show(dec.ToString());
 
M

mikeb

Muni said:
oops thought he was using 'Decimal' ... and thats correct you can only us
the 'as' operator with reference types

Don't forget that Decimal is also a value type.
 
G

Grant Richins [MS]

Another option that I haven't seen anybody post yet is this:

object x = 15m; // use the m suffix to make it a decimal literal instead of
an integer literal
decimal d = 0;
d = x;

In general whenever you put a struct or value-type into an object you box
it. Then when you cast back to a value-type you have to cast back to the
exact same value-type before you can do any conversions (like integer to
decimal). So you basically have 2 options. do the conversion before boxing,
or do it after the boxing.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top