96

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.

Owen Blacker
  • 3,889
  • 2
  • 33
  • 68
Piotr Czapla
  • 23,150
  • 23
  • 90
  • 120
  • 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

11 Answers11

189

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
  • 134,265
  • 53
  • 250
  • 293
  • 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
36

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
  • 10,865
  • 3
  • 38
  • 74
Saul Dolgin
  • 8,046
  • 4
  • 35
  • 42
  • 6
    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
21

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,326
  • 2
  • 38
  • 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
10

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,193
  • 11
  • 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
  • 5
    I think one can call it with null value. object obj = null; obj.IsNumeric(); – wiero Jul 21 '14 at 06:45
  • 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
8

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
  • 8,202
  • 5
  • 54
  • 70
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
  • 155,903
  • 29
  • 200
  • 218
4

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
  • 96,855
  • 20
  • 193
  • 238
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
  • 927,783
  • 236
  • 2,422
  • 2,784
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
  • 114,633
  • 27
  • 187
  • 321
1

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
  • 4,915
  • 7
  • 40
  • 57
0

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
  • 32,442
  • 7
  • 92
  • 127
  • 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
  • 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