Перевірка, чи є об’єкт числом у C #


89

Я хотів би перевірити , якщо об'єкт є числом , так що .ToString()призведе до рядка , що містить цифри і +, -,.

Чи можливо це шляхом простої перевірки типу в .net (наприклад:) if (p is Number)?

Або я повинен перетворити на рядок, а потім спробувати розібрати вдвічі?

Оновлення: Для уточнення мого об’єкта це int, uint, float, double, і так далі, це не рядок. Я намагаюся зробити функцію, яка б серіалізувала будь-який об'єкт у xml, як це:

<string>content</string>

або

<numeric>123.3</numeric>

або застосувати виняток.


5
Здається, ви намагаєтеся написати свій власний XmlSerializer - що не так з одним постачальником від .NET- msdn.microsoft.com/en-us/library/… ?
RichardOD

2
Можливо, ви зможете обійти всю цю проблему, визначивши свій формат XML за допомогою XSD, а потім створивши об’єкт, в який ви зможете серіалізувати свої дані за допомогою доставленого інструменту XSD - msdn.microsoft.com/en-us/library/x6c1kb0s % 28VS.71% 29.aspx
Декстер

@RichardOD: Чи можу я використовувати серіалізацію xml для серіалізації об'єкта []? Мені потрібно, щоб викликати функцію Flash adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/…
Piotr Czapla

Відповіді:


181

Вам просто потрібно буде перевірити тип для кожного з основних числових типів.

Ось метод розширення, який повинен виконати цю роботу:

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/ будь-що інше.


Моїм об’єктом є int, short, uint, float, double або щось інше, що є числом
Piotr Czapla

@Piotr: Ах так. Здається, я вас неправильно зрозумів. Дивіться мою оновлену відповідь.
Noldorin

1
@Noldorin: насправді ваша попередня версія коду також буде працювати; просто додайте нульову перевірку та використовуйте value.ToString (). Тоді вам не потрібно перевіряти всі числові типи.
Фредрік Морк,

1
@Noldorin Сумно, що правильним рішенням є таке багатослівне :(.
Piotr Czapla

1
@Joe: Насправді це не мало би значення, оскільки ToString також використовував би сучасну культуру.
Noldorin

36

Взято з блогу Скотта Ганзельмана :

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

6
Проблема цього підходу полягає в тому, що якщо ви передасте рядок, який виглядає як число, він відформатує його. Для більшості людей це може бути нормально, але для мене це була пробка.
Роб Седжвік,

1
Ще однією потенційною проблемою цього є те, що ви не можете проаналізувати значення min / max для double. double.Parse(double.MaxValue.ToString())викликає OverflowException. Ви можете виправити це, надавши модифікатор туди-назад .ToString("R"), але в цьому випадку це перевантаження недоступне, Convert.ToString(...)оскільки ми не знаємо його типу. Я знаю, що це трохи бахрома, але я випадково натрапив на нього під час написання тестів для власного .IsNumeric()розширення. Моїм "рішенням" було додати swtich для перевірки типу перед спробою проаналізувати що-небудь, див. Мою відповідь на це питання щодо коду.
Бен,

21

Скористайтеся властивістю 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);
}

EDIT: Виправлено відповідно до коментарів. Загальні відомості було видалено, оскільки типи значень вікон .GetType (). Також включено виправлення значень, що допускають обнулення.


1
Загальна частина не дає вам тут зайвого, чи не так? ви отримуєте доступ лише до GetType (), який доступний на об'єкті ...
Пітер Ліллевольд

Це економить одну операцію коробки, якщо вона викликається для типу значення. Подумайте про багаторазове використання.
Kenan EK

1
Чому б не використовувати typeof (T) замість obj.GetType, таким чином ви не отримаєте NullReferenceException, якщо хтось передає нульовий тип посилання. Ви також можете встановити загальне обмеження на T, щоб приймати лише типи значень. Звичайно, ви починаєте мати багато інформації під час компіляції, якщо це робите.
Трілліан

objectі stringне є примітивними типами.
Ми всі Моніка

@jnylen: ця відповідь була досить давно. Я вважаю, що я щось тоді винайшов із рефлекторизованого фреймворкового джерела, але хто може сказати сьогодні ... Виправлена ​​відповідь.
Kenan EK

10

Вище є кілька чудових відповідей. Ось рішення «все в одному». Три перевантаження за різних обставин.

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

розгляньте можливість додавання нульової перевірки
wiero

Насправді не потрібна перевірка на нуль - як метод розширення ви не можете викликати його з нульовим значенням. Звичайно, хтось все ще міг би викликати як звичайну функцію, але це не очікуване використання методу розширення.
Мік Бруно,

5
Я думаю, що це можна назвати нульовим значенням. об'єкт obj = null; obj.IsNumeric ();
wiero

Дякуємо Вейро, це виправили. Не розумів, що виклик методу розширення з нульовим значенням можливий, але, звичайно, це можливо!
Мік Бруно

Я думаю, що в першому перевантаженні відсутня дужка в кінці: "return (x == null? False: IsNumeric (x.GetType ()));"
glenn garson

8

Замість того, щоб прокручувати свій власний, найнадійніший спосіб визначити, чи є вбудований тип числовим - це, мабуть, посилання Microsoft.VisualBasicта виклик Information.IsNumeric(object value). Реалізація обробляє ряд тонких випадків, таких як char[]рядки HEX та OCT.


Це повинно бути вгорі!
nawfal

4

Там є три різні концепції:

  • щоб перевірити , якщо він є числом (тобто (зазвичай в штучної упаковці) числове значення , саме по собі), перевірте тип з is- наприклад ,if(obj is int) {...}
  • перевірити, чи рядок можна проаналізувати як число; використанняTryParse()
  • але якщо об'єкт не є числом або рядком, але ви підозрюєте, що ToString()може дати щось, що виглядає як число, тоді зателефонуйте ToString() і розгляньте це як рядок

В обох перших двох випадках вам, мабуть, доведеться обробляти кожен числовий тип, який ви хочете підтримувати ( double/ decimal/ int) - кожен з них має різні діапазони та точність, наприклад.

Ви також можете подивитися регулярний вираз для швидкої грубої перевірки.


4

Якщо припустити, що ваш вхід - це рядок ...

Є 2 способи:

використовуйте Double.TryParse ()

double temp;
bool isNumber = Double.TryParse(input, out temp);

використовуйте Regex

 bool isNumber = Regex.IsMatch(input,@"-?\d+(\.\d+)?");

4

Ви можете використовувати такий код:

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

Якщо об'єкт є Int32, Single, і Doubleт.д. він буде виконувати перетворення. Крім того, реалізується рядок, IConvertibleале якщо рядок не перетворюється на подвійний, FormatExceptionбуде викинуто a .


Фактично рядки будуть проаналізовані, але якщо формат буде неправильним, буде викинуто FormatException.
alfoks,

@alfoks: Ви абсолютно праві, тому я оновив відповідь.
Martin Liversage

1

Якщо ваша вимога справді

.ToString () призведе до рядка, що містить цифри та +, - ,.

і ви хочете використовувати double.TryParse, тоді вам потрібно використовувати перевантаження, яке приймає параметр NumberStyles, і переконайтесь, що ви використовуєте інваріантну культуру.

Наприклад, для числа, яке може мати провідний знак, відсутність пробілів, що ведуть або закінчуються, немає роздільника тисяч і десяткового роздільника крапки, використовуйте:

NumberStyles style = 
   NumberStyles.AllowLeadingSign | 
   NumberStyles.AllowDecimalPoint | 
double.TryParse(input, style, CultureInfo.InvariantCulture, out result);

1

Під час написання власного 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 _);
}

Остерігайтеся типів, що дозволяють онулювати.
Гільєрмо Пранді,

0

Так, це працює:

object x = 1;
Assert.That(x is int);

Для числа з плаваючою комою вам доведеться перевірити, використовуючи тип з плаваючою точкою:

object x = 1f;
Assert.That(x is float);

Це спрацює, якщо об’єкт був цілим до того, як був явно або явно переданий об’єкту. У вашому прикладі магічне число 1 - це int, а потім імпліцит, введений у тип змінної x .. Якби ви зробили об'єкт x = 1.0, ваше твердження повернуло б false.
Декстер

Є числа, які не є ints.
Фредрік Морк,

так, тому моя суть полягає в основному в тому, що зараз має відповідь @Noldorin.
Пітер Ліллевольд,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.