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.
This question is related to
c#
.net
serialization
xml-serialization
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);
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.
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.
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+)?");
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);
}
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);
There are three different concepts there:
is
- for example if(obj is int) {...}
TryParse()
ToString()
might give something that looks like a number, then call ToString()
and treat it as a stringIn 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.
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)); }
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 _);
}
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.
Source: Stackoverflow.com