Checking if an object is a number in C#

Viewed 151081

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.

12 Answers

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)
{
    return value is sbyte
            || value is byte
            || value is short
            || value is ushort
            || value is int
            || value is uint
            || value is long
            || value is ulong
            || value is float
            || value is double
            || value is decimal;
}

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.

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);
}

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

public static bool IsNumber(this object obj)
{
    if (Equals(obj, null))
    {
        return false;
    }

    Type objType = obj.GetType();
    objType = Nullable.GetUnderlyingType(objType) ?? objType;

    if (objType.IsPrimitive)
    {
        return objType != typeof(bool) && 
            objType != typeof(char) && 
            objType != typeof(IntPtr) && 
            objType != typeof(UIntPtr);
    }

    return objType == typeof(decimal);
}

EDIT: Fixed as per comments. The generics were removed since .GetType() boxes value types. Also included fix for nullable values.

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+)?");

You could use code like this:

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

If your object is an Int32, Single, Double etc. it will perform the conversion. Also, a string implements IConvertible but if the string isn't convertible to a double then a FormatException will be thrown.

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.

While writing my own object.IsNumeric() extension method based on Saul Dolgin's answer to this question I ran into a potential issue in that you will get an OverflowException if you try it with double.MaxValue or double.MinValue.

My "solution" was to combine the accepted answer from Noldorin with the one from Saul Dolgin and add a pattern matching switch before trying to parse anything (and use some C#7 goodness to tidy up a bit):

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

    switch (obj)
    {
        case sbyte _: return true;
        case byte _: return true;
        case short _: return true;
        case ushort _: return true;
        case int _: return true;
        case uint _: return true;
        case long _: return true;
        case ulong _: return true;
        case float _: return true;
        case double _: return true;
        case decimal _: return true;
    }

    string s = Convert.ToString(obj, CultureInfo.InvariantCulture);

    return double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out double _);
}

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);

Here is an even shorter version of @Noldorin's excellent answer:

public static Boolean IsNumeric(this Object obj) =>
  obj is SByte or Byte or Int16 or UInt16 or Int32 or UInt32 or Int64 or UInt64 or Single or Double or Decimal;

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);
Related