Memeriksa apakah suatu objek adalah angka di C #

91

Saya ingin memeriksa apakah sebuah objek adalah angka sehingga .ToString()akan menghasilkan string yang berisi angka dan +, -,.

Apakah mungkin dengan tipe check in sederhana .net (seperti:) if (p is Number)?

Atau Haruskah saya mengonversi menjadi string, lalu mencoba menguraikan menjadi ganda?

Pembaruan: Untuk memperjelas objek saya adalah int, uint, float, double, dan seterusnya itu bukan string. Saya mencoba membuat fungsi yang akan membuat objek apa pun menjadi xml seperti ini:

<string>content</string>

atau

<numeric>123.3</numeric>

atau ajukan pengecualian.

Piotr Czapla
sumber
5
Kedengarannya seperti Anda mencoba menulis XmlSerializer Anda sendiri- apa yang salah dengan satu penyedia oleh .NET- msdn.microsoft.com/en-us/library/… ?
RichardOD
2
Anda mungkin dapat mengatasi seluruh masalah ini dengan menentukan format XML Anda menggunakan XSD, lalu membuat objek yang dapat digunakan untuk menyusun data Anda menggunakan alat XSD yang dikirimkan - msdn.microsoft.com/en-us/library/x6c1kb0s % 28VS.71% 29.aspx
Dexter
@RichardOD: Dapatkah saya menggunakan serialisasi xml untuk membuat serialisasi objek []? Saya membutuhkannya untuk memanggil fungsi Flash adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/…
Piotr Czapla

Jawaban:

183

Anda hanya perlu melakukan pemeriksaan tipe untuk setiap tipe numerik dasar.

Berikut adalah metode ekstensi yang seharusnya melakukan pekerjaan itu:

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

Ini harus mencakup semua tipe numerik.

Memperbarui

Tampaknya Anda benar-benar ingin mengurai nomor dari string selama deserialisasinya. Dalam hal ini, mungkin lebih baik digunakan double.TryParse.

string value = "123.3";
double num;
if (!double.TryParse(value, out num))
    throw new InvalidOperationException("Value is not a number.");

Tentu saja, ini tidak akan menangani bilangan bulat / desimal yang sangat besar, tetapi jika itu masalahnya, Anda hanya perlu menambahkan panggilan tambahan ke long.TryParse/ decimal.TryParse/ apa pun.

Noldorin
sumber
Objek saya adalah int, short, uint, float, double atau apa pun yang merupakan angka
Piotr Czapla
@Piotr: Ah benar. Sepertinya saya salah paham tentang Anda. Lihat jawaban terbaru saya.
Noldorin
1
@Noldorin: sebenarnya versi kode Anda sebelumnya juga akan berfungsi; cukup tambahkan pemeriksaan null dan gunakan value.ToString (). Maka Anda tidak perlu memeriksa semua tipe numerik.
Fredrik Mörk
1
@Noldorin Sangat menyedihkan bahwa solusi yang tepat adalah bertele-tele :(.
Piotr Czapla
1
@Joe: Sebenarnya, itu tidak akan membuat perbedaan, karena ToString juga akan menggunakan budaya saat ini.
Noldorin
36

Diambil dari Blog Scott Hanselman :

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);
}
Saul Dolgin
sumber
6
Masalah dengan pendekatan ini adalah jika Anda mengirimkan string yang terlihat seperti angka, itu akan memformatnya. Mungkin baik-baik saja bagi kebanyakan orang, tetapi itu adalah penghenti acara bagi saya.
Rob Sedgwick
1
Masalah potensial lainnya dengan ini adalah Anda tidak dapat mengurai nilai min / max untuk double. double.Parse(double.MaxValue.ToString())menyebabkan sebuah OverflowException. Anda dapat memperbaikinya dengan menyediakan pengubah bolak-balik .ToString("R")dalam kasus ini, tetapi kelebihan beban tersebut tidak tersedia karena Convert.ToString(...)kami tidak tahu jenisnya. Saya tahu ini sedikit kasus pinggiran, tetapi saya tersandung ke dalamnya saat menulis tes untuk .IsNumeric()ekstensi saya sendiri . "Solusi" saya adalah menambahkan swtich jenis pemeriksaan sebelum mencoba mengurai apa pun, lihat jawaban saya untuk pertanyaan ini untuk kode.
Ben
21

Manfaatkan properti IsPrimitive untuk membuat metode ekstensi praktis:

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: Diperbaiki sesuai komentar. Obat generik telah dihapus sejak jenis nilai kotak .GetType (). Juga termasuk perbaikan untuk nilai nullable.

Kenan EK
sumber
1
Bagian generik tidak memberi Anda tambahan apa pun di sini, bukan? Anda hanya mengakses GetType () yang tersedia di objek ...
Peter Lillevold
Ini menghemat satu operasi kotak jika dipanggil pada tipe nilai. Pikirkan dapat digunakan kembali.
Kenan EK
1
Mengapa tidak menggunakan typeof (T) alih-alih obj.GetType, dengan cara itu Anda tidak akan mendapatkan NullReferenceException jika seseorang meneruskan tipe referensi null. Anda juga dapat menempatkan batasan umum pada T untuk hanya menerima tipe nilai. Tentu saja Anda mulai memiliki banyak informasi pada waktu kompilasi jika Anda melakukannya.
Trillian
objectdan stringbukan tipe primitif.
We Are All Monica
@jnylen: jawaban ini sudah cukup lama. Saya percaya saya menggali sesuatu dari sumber kerangka kerja reflektor pada saat itu, tetapi siapa yang tahu hari ini ... Jawaban tetap.
Kenan EK
10

Ada beberapa jawaban bagus di atas. Berikut ini solusi lengkap. Tiga kelebihan beban untuk keadaan yang berbeda.

// 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
sumber
pertimbangkan untuk menambahkan cek nol
wiero
Tidak benar-benar membutuhkan pemeriksaan null - sebagai metode ekstensi, Anda tidak dapat memanggilnya dengan nilai null. Tentu saja seseorang masih bisa memanggil sebagai fungsi normal tetapi itu bukan penggunaan yang diharapkan dari metode ekstensi.
Mick Bruno
5
Saya pikir orang bisa menyebutnya dengan nilai nol. objek obj = null; obj.IsNumeric ();
wiero
Terima kasih Weiro, sudah memperbaikinya. Tidak menyadari metode ekstensi panggilan dengan nilai null itu mungkin tapi tentu saja!
Mick Bruno
Saya pikir kelebihan pertama kehilangan tanda kurung di akhir: "return (x == null? False: IsNumeric (x.GetType ()));"
glenn garson
8

Daripada menggulung sendiri, cara paling andal untuk mengetahui apakah tipe bawaan adalah numerik mungkin dengan referensi Microsoft.VisualBasicdan panggilan Information.IsNumeric(object value). Implementasinya menangani sejumlah kasus halus seperti char[]string HEX dan OCT.

satnhak
sumber
Ini harus di atas!
nawfal
4

Ada tiga konsep berbeda di sana:

  • untuk memeriksa apakah itu adalah angka (yaitu nilai numerik (biasanya kotak) itu sendiri), periksa jenisnya dengan is- misalnyaif(obj is int) {...}
  • untuk memeriksa apakah sebuah string dapat diuraikan sebagai angka; menggunakanTryParse()
  • tetapi jika objeknya bukan angka atau string, tetapi Anda curiga ToString()mungkin memberikan sesuatu yang terlihat seperti angka, maka panggil ToString() dan perlakukan sebagai string

Dalam kedua kasus pertama, Anda mungkin harus menangani secara terpisah setiap jenis numerik yang ingin Anda dukung ( double/ decimal/ int) - masing-masing memiliki rentang dan akurasi yang berbeda, misalnya.

Anda juga bisa melihat regex untuk pemeriksaan kasar cepat.

Marc Gravell
sumber
4

Dengan asumsi masukan Anda adalah string ...

Ada 2 cara:

gunakan Double.TryParse ()

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

gunakan Regex

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

Anda bisa menggunakan kode seperti ini:

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

Jika objek Anda adalah Int32, Single, Doubledll itu akan melakukan konversi. Juga, sebuah string mengimplementasikan IConvertibletetapi jika string tersebut tidak dapat diubah menjadi double maka a FormatExceptionakan dilempar.

Martin Liversage
sumber
Sebenarnya string akan diurai, tetapi jika tidak dalam format yang benar, FormatException dilempar.
alfoks
@alfoks: Anda benar sekali jadi saya telah memperbarui jawabannya.
Martin Liversage
1

Jika kebutuhan Anda benar-benar

.ToString () akan menghasilkan string yang berisi angka dan +, - ,.

dan Anda ingin menggunakan double.TryParse maka Anda perlu menggunakan overload yang mengambil parameter NumberStyles, dan pastikan Anda menggunakan budaya invarian.

Misalnya untuk angka yang mungkin memiliki tanda awal, tidak ada spasi di depan atau di belakang, tidak ada pemisah ribuan dan pemisah desimal titik, gunakan:

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

Saat menulis object.IsNumeric()metode ekstensi saya sendiri berdasarkan jawaban Saul Dolgin untuk pertanyaan ini, saya mengalami masalah potensial di mana Anda akan mendapatkan OverflowExceptionjika Anda mencobanya dengan double.MaxValueatau double.MinValue.

"Solusi" saya adalah menggabungkan jawaban yang diterima dari Noldorin dengan jawaban dari Saul Dolgin dan menambahkan sakelar pencocokan pola sebelum mencoba mengurai apa pun (dan menggunakan beberapa kebaikan C # 7 untuk sedikit merapikan):

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
sumber
Waspadalah terhadap tipe nullable.
Guillermo Prandi
0

Ya, ini berfungsi:

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

Untuk bilangan floating point, Anda harus menguji menggunakan tipe float:

object x = 1f;
Assert.That(x is float);
Peter Lillevold
sumber
Ini akan berfungsi jika objek adalah int sebelum secara implisit atau eksplisit dilemparkan ke objek. Dalam contoh Anda, angka ajaib 1 adalah int, dan kemudian implikasinya dilemparkan ke dalam tipe variabel x .. Jika Anda menyelesaikan objek x = 1.0, pernyataan Anda akan mengembalikan false.
Dexter
Ada angka yang bukan int.
Fredrik Mörk
ya, jadi maksud saya pada dasarnya adalah apa yang dimiliki @Noldorin dalam jawabannya sekarang.
Peter Lillevold