110

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.

thatguy
  • 21,059
  • 6
  • 30
  • 40
Piotr Czapla
  • 25,734
  • 24
  • 99
  • 122
  • 5
    Sounds like you are trying to write your own XmlSerializer- what is wrong with the one provider by .NET- http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx ? – RichardOD Jul 15 '09 at 11:01
  • 2
    You might be able to get around this whole problem by defining your XML format using an XSD, and then creating an object into which you can serialize your data using the XSD tool shipped - http://msdn.microsoft.com/en-us/library/x6c1kb0s%28VS.71%29.aspx – Dexter Jul 15 '09 at 11:07
  • @RichardOD: Can I use xml serialization to serialize object[] ? I need it to call Flash function https://www.adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=19_External_Interface_178_3.html#126216 – Piotr Czapla Jul 15 '09 at 16:13

15 Answers15

205

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.

Community
  • 1
  • 1
Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • My object is int, short, uint, float, double or anything else that is a number – Piotr Czapla Jul 15 '09 at 10:53
  • @Piotr: Ah right. It seems I misunderstood you. See my updated answer. – Noldorin Jul 15 '09 at 10:56
  • 1
    @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 Jul 15 '09 at 10:58
  • @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 Jul 15 '09 at 10:59
  • 1
    @Noldorin It is sad that it the right solution is that verbose :(. – Piotr Czapla Jul 15 '09 at 16:15
  • @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 Jul 15 '09 at 16:19
  • The double.TryParse sample should be using an invariant culture. – Joe Jul 18 '09 at 13:36
  • 1
    @Joe: Actually, it wouldn't make a difference, since ToString would also use the current culture. – Noldorin Jul 18 '09 at 17:34
  • Like it thanks +1, but I still prefer the Java implementation which all numbers implement a Number interface. – Andez Oct 10 '13 at 10:47
  • Needs an update to reflect new types - `System.Half` - `System.Int128` - `System.UInt128` (https://learn.microsoft.com/en-us/dotnet/api/system.half?view=net-7.0 | https://learn.microsoft.com/en-us/dotnet/api/system.int128?view=net-7.0 | https://learn.microsoft.com/en-us/dotnet/api/system.uint128?view=net-7.0 ) – Rand Random May 25 '23 at 11:13
39

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);
}
Noctis
  • 11,507
  • 3
  • 43
  • 82
Saul Dolgin
  • 8,624
  • 4
  • 37
  • 43
  • 7
    The problem with this approach is if you pass in a string which looks like a number it will format it. Might be ok for most people but it was a show stopper for me. – Rob Sedgwick Jan 20 '15 at 16:15
  • 1
    Another potential problem with this is that you cannot parse the min/max values for double. `double.Parse(double.MaxValue.ToString())` causes an `OverflowException`. You could remedy this by providing the roundtrip modifier `.ToString("R")` in this case, but that overload isn't available for `Convert.ToString(...)` as we don't know the type. I know this is a bit of a fringe case, but I stumbled into it while writing tests for my own `.IsNumeric()` extension. My "solution" was to add a type checking swtich before trying to parse anything, see my answer to this question for the code. – Ben Jun 28 '19 at 08:44
22

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.

Kenan E. K.
  • 13,955
  • 3
  • 43
  • 48
  • 1
    The generics part isn't giving you any extra here, is it? you only access GetType() which is available on object... – Peter Lillevold Jul 15 '09 at 12:31
  • It saves one box operation if being called on a value type. Think reusability. – Kenan E. K. Jul 15 '09 at 12:45
  • 1
    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 Jul 18 '09 at 13:59
  • `object` and `string` are not primitive types. – We Are All Monica Aug 21 '12 at 18:16
  • @jnylen: this answer was quite some time ago. I believe I dug somethin up from reflectorized framework source at the time, but who can tell today... Fixed answer. – Kenan E. K. Aug 21 '12 at 18:41
  • Necromancer Here: FYI, no need to check for `Decimal`, as the primitive form is `Double` and `Short` as per: [MSDN](http://msdn.microsoft.com/en-us/library/system.type.isprimitive(v=vs.110).aspx). Otherwise +1, helped me build an ObjectExtension method instead of one for each type. – GoldBishop Feb 21 '14 at 21:07
  • 1000M.IsNumber() is False – wiero Jul 17 '14 at 07:27
  • decimal is not a primitive. Another unintuitive false ((object)5).IsNumber() – wiero Jul 17 '14 at 07:44
11

There are some great answers above. Here is an all-in-one solution. Three overloads for different circumstances.

// Extension method, call for any object, eg "if (x.IsNumeric())..."
public static bool IsNumeric(this object x) { return (x==null ? false : IsNumeric(x.GetType())); }

// Method where you know the type of the object
public static bool IsNumeric(Type type) { return IsNumeric(type, Type.GetTypeCode(type)); }

// Method where you know the type and the type code of the object
public static bool IsNumeric(Type type, TypeCode typeCode) { return (typeCode == TypeCode.Decimal || (type.IsPrimitive && typeCode != TypeCode.Object && typeCode != TypeCode.Boolean && typeCode != TypeCode.Char)); }
Mick Bruno
  • 1,373
  • 15
  • 13
  • consider adding null check – wiero Jul 17 '14 at 07:55
  • Don't really need null check - as an extension method, you couldn't call it with a null value. Of course somebody could still call as a normal function but that is not the expected usage of an extension method. – Mick Bruno Jul 19 '14 at 05:19
  • 6
    I think one can call it with null value. object obj = null; obj.IsNumeric(); – wiero Jul 21 '14 at 06:45
  • 1
    Thanks Weiro, have fixed it. Didn't realize calling extension method with a null value was possible but of course it is! – Mick Bruno Jul 24 '14 at 21:02
  • I think the first overload is missing a parenthesis at the end: "return (x==null ? false : IsNumeric(x.GetType())); " – glenn garson Aug 28 '14 at 13:47
  • I think its a little risky in the 3rd function to assume that if the object is not one of those things, then therefore it is numeric. What if some future .NET includes new non-numeric primitives... this could yield a false positive. Compare to the builtin VB.NET IsNumeric which inverts the logic (more or less) https://stackoverflow.com/a/74044011/3195477 – StayOnTarget Oct 12 '22 at 15:01
11

Rather than rolling your own, the most reliable way to tell if an in-built type is numeric is probably to reference Microsoft.VisualBasic and call Information.IsNumeric(object value). The implementation handles a number of subtle cases such as char[] and HEX and OCT strings.

satnhak
  • 9,407
  • 5
  • 63
  • 81
  • 1
    This should be at the top! – nawfal Jun 26 '16 at 11:05
  • It would be good if it works. In my .net framework 4.7.2 it fails to parse UInt16, whereas help says that it should. Checked disassembly of Microsoft.VisualBasic.dll - if input is a string then it tries to convert it to double, otherwise it calls IsOldNumericTypeCode and checks by Int16 and some others but UInt16 and other unsigned types are ignored – sarh Oct 05 '22 at 14:08
  • So the most reliable way is to call VisualBasic? – E. van Putten Mar 08 '23 at 16:09
  • 1
    @E.vanPutten I wouldn't be using this today. – satnhak Mar 11 '23 at 16:42
6

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.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
4

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
  • 168,566
  • 31
  • 210
  • 223
4

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
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

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 _);
}
Ben
  • 5,525
  • 8
  • 42
  • 66
1

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
  • 122,218
  • 32
  • 205
  • 338
1

In .NET 7 a new API was introduced to support generic math, which offers lots of interfaces that are implemented in all numerical types. There is an interface INumber<TSelf> which can be used as type constraint for generic methods to only allow for numeric types (int, double, decimal, ...):

public void FooBar<T>(T number) where T : INumber<T>
{
   // ...number is a numeric type.
}

There are also more fine-grained interfaces for categories of numeric types:

  • IBinaryInteger<T> for byte, short, int, long, ...
  • IFloatingPoint<T> for float, double, decimal, ...
  • ...

This way, you can define one or more generic type constraints specifically tailored to the numeric types that you expect to handle in your methods along with the benefits of type safety. This should be your preferred route to take and should save you from expensive or faulty checks and passing object around.


If you must rely on object, you can at least use the new INumber<T> interface to check with reflection if it is a numeric type. You would check if the type implements any concrete INumber<T> interface, by comparing their generic type definitions to the open generic INumber<> type. Please be aware that reflection may incur performance costs that must be considered.

private bool IsNumericType(object obj)
{
   return obj
      .GetType()
      .GetInterfaces()
      .Any(@interface => @interface.GetGenericTypeDefinition() == typeof(INumber<>));
}

The API might provide other useful interfaces for your purpose, like for formatting and parsing.

thatguy
  • 21,059
  • 6
  • 30
  • 40
0

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;
David Liebeherr
  • 412
  • 6
  • 8
0

Don't overlook that .NET includes Microsoft.VisualBasic.Information.IsNumeric() which can be used from C# just as well as VB. This may be preferable to writing your own, or at least use it as a basis.


The way it works is to first look for certain types that can be "converted" to or parsed as numbers (strings, chars, or char arrays), and then finally just checks the type code to see if it is one of the intrinsic numeric types.

If you want to exclude strings or chars then you could use it like so:

if (foo.GetType().IsValueType && !(foo is char) && Information.IsNumeric(foo))
{
    // Numeric...
}

Reference source does not seem to include this, but it can be viewed in Visual Studio:

public static bool IsNumeric(object Expression)
{
    IConvertible convertible = Expression as IConvertible;
    if (convertible == null)
    {
        char[] array = Expression as char[];
        if (array == null)
        {
            return false;
        }

        Expression = new string(array);
    }

    TypeCode typeCode = convertible.GetTypeCode();
    if (typeCode == TypeCode.String || typeCode == TypeCode.Char)
    {
        string value = convertible.ToString(null);
        try
        {
            long i64Value = default(long);
            if (Utils.IsHexOrOctValue(value, ref i64Value))
            {
                return true;
            }
        }
        catch (StackOverflowException ex) { throw ex; }
        catch (OutOfMemoryException ex2) { throw ex2; }
        catch (ThreadAbortException ex3) { throw ex3; }
        catch (Exception) { return false; }

        double Result = default(double);
        return DoubleType.TryParse(value, ref Result);
    }

    return IsOldNumericTypeCode(typeCode);
}

They way it checks for explicit numeric types is similar to answer https://stackoverflow.com/a/18857243/3195477 although its logic is inverted in a way which may be more robust/safe: it only returns true for a known numeric type, as opposed to returning false for known non-numeric types; the latter could lead to a bug if ever other types are added.

internal static bool IsOldNumericTypeCode(TypeCode TypCode)
{
    switch (TypCode)
    {
        case TypeCode.Boolean:
        case TypeCode.Byte:
        case TypeCode.Int16:
        case TypeCode.Int32:
        case TypeCode.Int64:
        case TypeCode.Single:
        case TypeCode.Double:
        case TypeCode.Decimal:
            return true;
        default:
            return false;
    }
}
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
-1

You could do the check by type. Get the type with the typeof() operator.

private bool IsNumber(Type type)
{
   return type.IsPrimitive && type.IsValueType;
}
Pieter
  • 458
  • 5
  • 16
-2

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
  • 33,668
  • 7
  • 97
  • 131
  • 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 Jul 15 '09 at 10:56
  • 1
    There are numbers that are not ints. – Fredrik Mörk Jul 15 '09 at 10:56
  • yes, so my point is basically what @Noldorin have in his answer right now. – Peter Lillevold Jul 15 '09 at 10:58