tags:

views:

740

answers:

8

I'd like to check if an object is a number so that .ToString() would result in a string containing digits and +,-,.

Is it possible by simple type checking in .net (like: if (p is Number))?

Or Should I convert to string, then try parsing to double?

Update:' To clarify my object is int, uint, float, double, and so on it isn't a string. I'm trying to make a function that would serialize any object to xml like this:

<string>content</string>

or

<numeric>123.3</numeric>

or raise an exception.

+16  A: 

You will simply need to do a type check for each of the basic numeric types.

Here's an extension method that should do the job:

public static bool IsNumber(this object value)
{
    if (value is sbyte) return true;
    if (value is byte) return true;
    if (value is short) return true;
    if (value is ushort) return true;
    if (value is int) return true;
    if (value is uint) return true;
    if (value is long) return true;
    if (value is ulong) return true;
    if (value is float) return true;
    if (value is double) return true;
    if (value is decimal) return true;
    return false;
}

This should cover all numeric types.

Update

It seems you do actually want to parse the number from a string during deserialisation. In this case, it would probably just be best to use double.TryParse.

string value = "123.3";
double num;
if (!double.TryParse(value, out num))
    throw new InvalidOperationException("Value is not a number.");

Of course, this wouldn't handle very large integers/long decimals, but if that is the case you just need to add additional calls to long.TryParse / decimal.TryParse / whatever else.

Noldorin
My object is int, short, uint, float, double or anything else that is a number
Piotr Czapla
@Piotr: Ah right. It seems I misunderstood you. See my updated answer.
Noldorin
@Noldorin: actually your previous version of the code would work as well; just add a null check and use value.ToString(). Then you don't need to check for all the numeric types.
Fredrik Mörk
@Fredrik: That's true. I guess... It may be marginally shorter to read, but I think you'll agree it's a bit of a hack. It certainly is much less efficient.
Noldorin
You missed byte and sbyte.
kek444
@kek444: You're right. I knew something was absent...
Noldorin
@Noldorin It is sad that it the right solution is that verbose :(.
Piotr Czapla
@Piotr: You could say that... although once it's implemented as an extension method, it's very easy to use. :) I think it's the nature of a static language that means checks have to be performed this way.
Noldorin
The double.TryParse sample should be using an invariant culture.
Joe
@Joe: Actually, it wouldn't make a difference, since ToString would also use the current culture.
Noldorin
+1  A: 

Yes, this works:

object x = 1;
Assert.That(x is int);

For a floating point number you would have to test using the float type:

object x = 1f;
Assert.That(x is float);
Peter Lillevold
would it work if x = 1.0
Piotr Czapla
This will work if the object was an int before being implicitly or explicitly cast to an object. In your example, the magic number 1 is an int, and is then implicity cast into the type of the variable x.. If you'd done object x = 1.0, your assert would have returned false.
Dexter
There are numbers that are not ints.
Fredrik Mörk
yes, so my point is basically what @Noldorin have in his answer right now.
Peter Lillevold
A: 

Assuming your input is a string...

There are 2 ways:

use Double.TryParse()

double temp;
bool isNumber = Double.TryParse(input, out temp);

use Regex

 bool isNumber = Regex.IsMatch(input,@"-?\d+(\.\d+)?");
Philippe Leybaert
A: 

You could use code like this:

if (n is IConvertible)
  return ((IConvertible) n).ToDouble(CultureInfo.CurrentCulture);
else
  // Cannot be converted.

If your object is a string this will not parse the string. However, if your object is an Int32, Single, Double etc. it will perform the conversion.

Martin Liversage
+2  A: 

There are three different concepts there:

  • to check if it is a number (i.e. a (typically boxed) numeric value itself), check the type with is - for example if(obj is int) {...}
  • to check if a string could be parsed as a number; use TryParse()
  • but if the object isn't a number or a string, but you suspect ToString() might give something that looks like a number, then call ToString() and treat it as a string

In both the first two cases, you'll probably have to handle separately each numeric type you want to support (double/decimal/int) - each have different ranges and accuracy, for example.

You could also look at regex for a quick rough check.

Marc Gravell
+12  A: 

Taken from Scott Hanselman's Blog:

public static bool IsNumeric(object expression)
{
    if (expression == null)
    return false;

    double number;
    return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
}
Saul Dolgin
+3  A: 

Take advantage of the IsPrimitive property and generics to make a handy extension method:

public static bool IsNumber<T>(this T obj)
{
    Type objType = obj.GetType();

    if(objType.IsPrimitive)
    {
        if (objType == typeof(object) || 
            objType == typeof(string) || 
            objType == typeof(bool))
            return false;

        return true;
    }

    return false;
}
kek444
The generics part isn't giving you any extra here, is it? you only access GetType() which is available on object...
Peter Lillevold
It saves one box operation if being called on a value type. Think reusability.
kek444
Why not use typeof(T) instead of obj.GetType, that way you won't get a NullReferenceException if someone passes a null reference type. You could also put a generic constrain on T to accept only value types. Of course you start having a lot of information at compile time if you do that.
Trillian
A: 

If your requirement is really

.ToString() would result in a string containing digits and +,-,.

and you want to use double.TryParse then you need to use the overload that takes a NumberStyles parameter, and make sure you are using the invariant culture.

For example for a number which may have a leading sign, no leading or trailing whitespace, no thousands separator and a period decimal separator, use:

NumberStyles style = 
   NumberStyles.AllowLeadingSign | 
   NumberStyles.AllowDecimalPoint | 
double.TryParse(input, style, CultureInfo.InvariantCulture, out result);
Joe