Проверка, является ли объект числом в C#
Я хотел бы проверить, является ли объект числом, чтобы .ToString()
приведет к строке, содержащей цифры и +
,-
,.
Возможно ли это путем простой проверки типов в.net (например: if (p is Number)
)?
Или я должен преобразовать в строку, а затем попробовать разбор в два раза?
Обновление: чтобы уточнить, что мой объект это int, uint, float, double, и так далее, это не строка. Я пытаюсь сделать функцию, которая сериализует любой объект в XML, как это:
<string>content</string>
или же
<numeric>123.3</numeric>
или поднять исключение.
15 ответов
Вам просто нужно будет выполнить проверку типов для каждого из основных числовых типов.
Вот метод расширения, который должен делать эту работу:
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;
}
Это должно охватывать все числовые типы.
Обновить
Кажется, вы действительно хотите разобрать номер из строки во время десериализации. В этом случае, вероятно, было бы лучше всего использовать double.TryParse
,
string value = "123.3";
double num;
if (!double.TryParse(value, out num))
throw new InvalidOperationException("Value is not a number.");
Конечно, это не будет обрабатывать очень большие целые / длинные десятичные числа, но если это так, вам просто нужно добавить дополнительные вызовы long.TryParse
/ decimal.TryParse
/ что угодно.
Взято из блога Скотта Хансельмана:
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);
}
Воспользуйтесь преимуществом свойства IsPrimitive, чтобы создать удобный метод расширения:
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);
}
РЕДАКТИРОВАТЬ: Исправлено в соответствии с комментариями. Обобщения были удалены, так как.GetType() содержит типы значений. Также включено исправление для значений Nullable.
Вместо того, чтобы бросать свой собственный, самый надежный способ определить, является ли встроенный тип числовым, - это, вероятно, ссылка Microsoft.VisualBasic
и позвонить Information.IsNumeric(object value)
, Реализация обрабатывает ряд тонких случаев, таких как char[]
и строки HEX и OCT.
Есть несколько отличных ответов выше. Вот решение "все в одном". Три перегрузки для разных обстоятельств.
// 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)); }
Вы можете использовать такой код:
if (n is IConvertible)
return ((IConvertible) n).ToDouble(CultureInfo.CurrentCulture);
else
// Cannot be converted.
Если ваш объект Int32
, Single
, Double
и т.д. он будет выполнять преобразование. Кроме того, строка реализует IConvertible
но если строка не может быть преобразована в двойной, то FormatException
будет брошен.
Предполагая, что ваш ввод является строкой...
Есть 2 способа:
использовать Double.TryParse()
double temp;
bool isNumber = Double.TryParse(input, out temp);
использовать Regex
bool isNumber = Regex.IsMatch(input,@"-?\d+(\.\d+)?");
Там есть три разных понятия:
- чтобы проверить, является ли это число (то есть само (обычно в штучной упаковке) числовым значением), проверьте тип с
is
- напримерif(obj is int) {...}
- проверить, может ли строка быть проанализирована как число; использование
TryParse()
- но если объект не является числом или строкой, но вы подозреваете,
ToString()
может дать что-то похожее на число, а затем позвонитьToString()
и рассматривать это как строку
В обоих первых двух случаях вам, вероятно, придется обрабатывать отдельно каждый числовой тип, который вы хотите поддерживать (double
/ decimal
/ int
) - у каждого разные диапазоны и точность, например.
Вы также можете посмотреть на регулярные выражения для быстрой грубой проверки.
В .NET 7 был представлен новый API для поддержки универсальной математики , который предлагает множество интерфейсов, реализованных во всех числовых типах. Есть интерфейсINumber<TSelf>
который можно использовать в качестве ограничения типа для универсальных методов, чтобы разрешить только числовые типы (, , ,...):
public void FooBar<T>(T number) where T : INumber<T>
{
// ...number is a numeric type.
}
Существуют также более детализированные интерфейсы для категорий числовых типов:
-
IBinaryInteger<T>
дляbyte
,short
,int
,long
, ... -
IFloatingPoint<T>
дляfloat
,double
,decimal
, ... - ...
Таким образом, вы можете определить одно или несколько ограничений универсального типа, специально адаптированных для числовых типов, которые вы ожидаете обрабатывать в своих методах, а также преимущества безопасности типов. Это должен быть ваш предпочтительный маршрут, и он должен уберечь вас от дорогих или ошибочных чеков и обхода.
Если вы должны полагаться наobject
, вы можете по крайней мере использовать новый интерфейс, чтобы проверить с помощью отражения, является ли он числовым типом. Вы бы проверили, реализует ли тип какой-либо конкретныйINumber<T>
интерфейса, сравнивая их общие определения типов с открытыми универсальнымиINumber<>
тип. Имейте в виду, что отражение может повлечь за собой затраты на производительность, которые необходимо учитывать.
private bool IsNumericType(object obj)
{
return obj
.GetType()
.GetInterfaces()
.Any(@interface => @interface.GetGenericTypeDefinition() == typeof(INumber<>));
}
API может предоставить другие полезные интерфейсы для ваших целей, например для форматирования и синтаксического анализа .
Пока я пишу свой object.IsNumeric()
метод расширения, основанный на ответе Сола Долгина на этот вопрос. Я столкнулся с потенциальной проблемой, заключающейся в том, что вы получите OverflowException
если вы попробуете это с double.MaxValue
или double.MinValue
.
Мое "решение" заключалось в том, чтобы объединить принятый ответ от Нолдорина с ответом Саула Долгина и добавить переключатель сопоставления с образцом, прежде чем пытаться что-либо проанализировать (и использовать некоторую доброту C#7, чтобы немного привести в порядок):
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 _);
}
Если ваше требование действительно
.ToString() приведет к строке, содержащей цифры и +,-,.
и вы хотите использовать double.TryParse, тогда вам нужно использовать перегрузку, которая принимает параметр NumberStyles, и убедиться, что вы используете инвариантную культуру.
Например, для числа, которое может иметь начальный знак, без начального или конечного пробела, без разделителя тысяч и десятичного разделителя периода, используйте:
NumberStyles style =
NumberStyles.AllowLeadingSign |
NumberStyles.AllowDecimalPoint |
double.TryParse(input, style, CultureInfo.InvariantCulture, out result);
Да, это работает:
object x = 1;
Assert.That(x is int);
Для числа с плавающей запятой вы должны будете проверить, используя тип с плавающей запятой:
object x = 1f;
Assert.That(x is float);
Не забывайте, что .NET включает в себя Microsoft.VisualBasic.Information.IsNumeric() , который можно использовать из C# точно так же, как и из VB. Это может быть предпочтительнее написания собственного или, по крайней мере, использовать его в качестве основы.
Это работает следующим образом: сначала ищутся определенные типы, которые могут быть «преобразованы» в числа или проанализированы как числа (строки, символы или массивы символов), а затем, наконец, просто проверяется код типа, чтобы увидеть, является ли он одним из встроенных числовых значений. типы.
Если вы хотите исключить строки или символы, вы можете использовать это так:
if (foo.GetType().IsValueType && !(foo is char) && Information.IsNumeric(foo))
{
// Numeric...
}
Справочный источник, похоже, не включает это, но его можно просмотреть в 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);
}
То, как он проверяет явные числовые типы, похоже на ответ /questions/38163542/proverka-yavlyaetsya-li-obekt-chislom-v-c/38163560#38163560 , хотя его логика инвертируется таким образом, который может быть более надежным/безопасным: он возвращает толькоtrue
для известного числового типа, в отличие от возвратаfalse
для известных нечисловых типов; последнее может привести к ошибке, если будут добавлены другие типы.
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;
}
}
Вот еще более короткая версия отличного ответа @Noldorin:
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;
Вы можете выполнить проверку по типу. Получите тип с помощью оператора typeof().
private bool IsNumber(Type type)
{
return type.IsPrimitive && type.IsValueType;
}