views:

623

answers:

3
+2  Q: 

C# boxing question

First, two examples:

// This works
int foo = 43;
long lFoo = foo;

// This doesn't
object foo = (int)43;
long? nullFoo = foo as long?; // returns null
long lFoo = (long)foo; // throws InvalidCastException
if (foo.GetType() == typeof(int))
    Console.WriteLine("But foo is an int..."); // This gets written out

Now, my guess as to why the second doesn't work is because of boxing. The purpose behind this code is to implement IComparable. I need some way to coerce an object into either a long or a ulong as appropriate, or if it's neither, than to throw an error. I don't want to have to implement checks for each basic numeric type (byte, int, long, ubyte, ...) I'd rather just catch them in the largest numeric type and deal with it that way. Thoughts from all the smart people here? How can I unbox the object, preferably avoiding reflection, but I suppose if that's the only way... Or should I just not implement the non-generics version of IComparable?

Edit:

This seems to work, but seems like a horrible hack around the problem. Is it just me?

long lFoo = long.Parse(foo.ToString());
+4  A: 

When you're casting to a value type you're really forcing an unbox IL operation, which requires that the type you're casting to matches exactly the boxed value; there are no conversions, implicit, or explicit that can happen at the same time.

This usually means that you either need to do a switch using the typecode (or an if/else if using types), or, in your case, go with a check for null followed by Convert.ToInt64(), which should deal with it correctly.

tomasr
It doesn't actually require an *exact* match. You can unbox a boxed enum as its underlying type or vice versa (despite the type information really being there). You can also unbox to an appropriate nullable type. There are some similar odd conversions which the CLR allows, like int[] to uint[].
Jon Skeet
A: 

Its not just you, however tryparse does not raise an exception.

object foo = (int)43;
long outVal;
if(long.TryParse(foo.ToString(),out outVal))
{
//take action with correct value of long
}
else
{
//maybe passed you another type of object
}
Nat
This is *not* the way to go. Besides being a lot more inefficient, I believe there's no guarantee that the string format generated by ToString() can actually be parsed right away by Parse/TryParse() (i.e. not in all curtures it will roundtrip)
tomasr
+6  A: 
object foo  = (int) 43;
long   lFoo = ((IConvertible) foo).ToInt64(null);
Mark Cidade
This actually works... Hurray for base types that inherit interfaces. Never quite understood that part, but useful none the less.
Matthew Scharley