Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I not cast a smallint from MSSQL to int like this: int x = (int)dt.Rows[x]["smallIntColumn"]

Tags:

c#

casting

Just looking for an explanation on this - the question pretty much explains it all but here's an bit extra:

//this line works OK
int i = Convert.ToInt32(dt.Rows[x]["SmallintColumnName"]);

//this line errors out
int j = (int)dt.Rows[x]["SmallintColumnName"];

I find this strange but I guess there's a valid argument on why this is like it is.

Thanks.

EDIT: Hi @Damien_The_Unbeliever - it is an InvalidCastException - Specified cast is not valid. I guess I need to read up on boxing and unboxing. I assumed that both would work. Now I have just tried:

int k = (Int32)r_dtAttribute.Rows[x]["CultureId"];

this also fails with the same InvalidCastException - so can anyone tell me what the actual difference is between

//fails
int k = (Int32)r_dtAttribute.Rows[x]["CultureId"];

and

//works
int i = Convert.ToInt32(dt.Rows[x]["SmallintColumnName"]);

What does the Convert.ToInt32 function do that just casting with (Int32) doesn't do?

Thanks

like image 830
Percy Avatar asked Jan 09 '14 11:01

Percy


1 Answers

dt.Rows[x]["SmallintColumnName"] is going to return an object. What the database returned has actually been translated (by ADO.Net) into an Int161, but since the return value of the expression is object, the value has been boxed.

I linked in a comment to the article on Boxing and Unboxing. Your expression:

int j = (int)dt.Rows[x]["SmallintColumnName"];

Is an attempt to unbox a value from an object. But the rules of boxing and unboxing say that you can only unbox exactly the same type that was boxed (except some weird rules to do with enums and their underlying types, IIRC). But since we know that what we have is a boxed Int16, you're going to get an InvalidCastException.

What you could do is:

int j = (int)(Int16)dt.Rows[x]["SmallintColumnName"];

or even just:

int j = (Int16)dt.Rows[x]["SmallintColumnName"];

Where we first unbox the Int16 value and then explicitly (first example) or implicitly (second) cast the Int16 to an Int32. It's slightly unfortunate that casting and unboxing look exactly the same.

Convert.ToInt32(object) is a whole different beast and supports all kinds of conversions. What it actually does is to cast the argument to the IConvertible interface (which Int16 supports) and then calls the ToInt32 method on the Int16. That, in turn, turns around and calls Convert.ToInt32(Int16), having switched back from being a boxed Int16 to just being a plain Int16, which finally gets implicitly converted into an Int32. Magic.


1 I've tried, but I think failed, to be consistent throughout the answer in the type names I use. But be aware that short and Int16 are exactly the same type. int and Int32 are also the same types. So switching your attempt from (int) to (Int32) didn't actually cause any change in the compiled code.

like image 54
Damien_The_Unbeliever Avatar answered Nov 15 '22 00:11

Damien_The_Unbeliever