Bir nesnenin C # 'da bir sayı olup olmadığını kontrol etme


91

O kadar bir nesne bir sayı olup olmadığını kontrol etmek istiyorum .ToString()basamak içeren bir dize neden olacaktır +, -,.

.Net'te basit bir tür denetleme ile mümkün mü (gibi:) if (p is Number)?

Veya dizeye dönüştürmeli ve sonra ikiye katlamayı denemeli miyim?

Güncelleme: Nesnemin int, uint, float, double ve benzeri olduğunu açıklığa kavuşturmak için bir dizge değil. Herhangi bir nesneyi şu şekilde xml'ye serileştirecek bir işlev yapmaya çalışıyorum:

<string>content</string>

veya

<numeric>123.3</numeric>

veya bir istisna yaratın.


5
Kendi XmlSerializer'ınızı yazmaya çalışıyorsunuz gibi görünüyor - .NET- msdn.microsoft.com/en-us/library/… tarafından sağlanan bir sağlayıcının sorunu nedir?
RichardOD

2
XML biçiminizi bir XSD kullanarak tanımlayarak ve ardından gönderilen XSD aracını kullanarak verilerinizi serileştirebileceğiniz bir nesne oluşturarak tüm bu sorunu aşabilirsiniz
Dexter

@RichardOD: Nesneyi [] serileştirmek için xml serileştirmeyi kullanabilir miyim? Flash işlevini çağırmak için ona ihtiyacım var adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/…
Piotr Czapla

Yanıtlar:


183

Temel sayısal türlerin her biri için bir tür kontrolü yapmanız gerekecektir.

İşte işi yapması gereken bir uzatma yöntemi:

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

Bu, tüm sayısal türleri kapsamalıdır.

Güncelleme

Görünüşe göre seriyi kaldırma sırasında aslında bir dizedeki sayıyı ayrıştırmak istiyorsunuz. Bu durumda, muhtemelen kullanmak en iyisi olacaktır double.TryParse.

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

Tabii ki, bu çok büyük tam sayıları / uzun ondalık sayıları işleyemez, ancak bu durumda, sadece long.TryParse/ decimal.TryParse/ başka herhangi bir şeye ek çağrı eklemeniz gerekir .


Nesnem int, short, uint, float, double veya sayı olan herhangi bir şey
Piotr Czapla

@Piotr: Ah doğru. Görünüşe göre seni yanlış anladım. Güncellenen cevabıma bakın.
Noldorin

1
@Noldorin: aslında kodun önceki sürümünüz de işe yarayacaktı; sadece boş bir kontrol ekleyin ve değer kullanın.ToString (). O zaman tüm sayısal türleri kontrol etmenize gerek yoktur.
Fredrik Mörk

1
@Noldorin Doğru çözümün bu kadar ayrıntılı olması üzücü :(.
Piotr Czapla

1
@Joe: Aslında, ToString de mevcut kültürü kullanacağı için bu bir fark yaratmaz.
Noldorin

36

Alındığı Scott Hanselman'ın 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);
}

6
Bu yaklaşımla ilgili sorun, sayıya benzeyen bir dizge geçerseniz, onu biçimlendirmesidir. Çoğu insan için iyi olabilir ama benim için bir gösteri durdurucusuydu.
Rob Sedgwick

1
Bununla ilgili diğer bir olası sorun, double için minimum / maksimum değerleri ayrıştıramamanızdır. double.Parse(double.MaxValue.ToString())neden olur bir OverflowException. .ToString("R")Bu durumda, gidiş dönüş değiştiricisini sağlayarak bunu çözebilirsiniz , ancak Convert.ToString(...)türünü bilmediğimiz için bu aşırı yükleme mevcut değildir. Bunun biraz saçma bir durum olduğunu biliyorum, ancak kendi .IsNumeric()uzantım için testler yazarken tökezledim . Benim "çözümüm", herhangi bir şeyi ayrıştırmaya çalışmadan önce bir tür kontrol anahtarı eklemekti, kod için bu soruya cevabıma bakın.
Ben

21

Kullanışlı bir genişletme yöntemi oluşturmak için IsPrimitive özelliğinden yararlanın:

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

DÜZENLEME: Yorumlara göre düzeltildi. .GetType () kutuları değer türlerinden beri jenerikler kaldırılmıştır. Ayrıca null yapılabilir değerler için düzeltme dahil edildi.


1
Jenerik kısım size fazladan bir şey vermiyor, değil mi? nesne üzerinde kullanılabilir yalnızca erişim GetType (...)
Peter Lillevold

Bir değer türünde çağrılıyorsa bir kutu işlemi kaydeder. Yeniden kullanılabilirliği düşünün.
Kenan EK

1
Neden obj.GetType yerine typeof (T) kullanmıyorsunuz, bu şekilde birisi boş bir başvuru türü geçirirse NullReferenceException almazsınız. Yalnızca değer türlerini kabul etmesi için T'ye genel bir sınırlama da koyabilirsiniz. Tabii ki bunu yaparsanız derleme sırasında çok fazla bilgi edinmeye başlarsınız.
Trillian

objectve stringilkel türler değildir.
We Are All Monica

@jnylen: Bu cevap epey zaman önceydi. O zamanlar reflektörlü çerçeve kaynağından bir şeyler çıkardığıma inanıyorum, ama bugünü kim söyleyebilir ... Sabit cevap.
Kenan EK

10

Yukarıda bazı harika cevaplar var. İşte hepsi bir arada bir çözüm. Farklı durumlar için üç aşırı yükleme.

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

boş onay eklemeyi düşünün
wiero

Null kontrolüne gerçekten gerek yok - bir uzantı yöntemi olarak, onu boş bir değerle arayamazsınız. Elbette birisi yine de normal bir işlev olarak çağırabilir, ancak bu bir uzantı yönteminin beklenen kullanımı değildir.
Mick Bruno

5
Sanırım buna boş değer diyebiliriz. nesne obj = null; obj.IsNumeric ();
wiero

Teşekkürler Weiro, düzelttim. Boş değerli uzantı yöntemini çağırmanın mümkün olduğunu fark etmemiştim ama elbette öyle!
Mick Bruno

İlk aşırı yüklemenin sonunda bir parantez eksik olduğunu düşünüyorum: "return (x == null? False: IsNumeric (x.GetType ()));"
glenn garson

8

Kendinizinkini çevirmek yerine, yerleşik bir türün sayısal olup olmadığını anlamanın en güvenilir yolu muhtemelen başvurmak Microsoft.VisualBasicve aramaktır Information.IsNumeric(object value). Uygulama, char[]HEX ve OCT dizeleri gibi bir dizi ince durumu ele alır .


Bu en üstte olmalı!
nawfal

4

Orada üç farklı kavram var:

  • Bu kontrol etmek olan bir sayı (yani, bir (tipik olarak kutu) sayısal bir değer kendisi) ile tipi kontrol is- örneğinif(obj is int) {...}
  • bir dizenin sayı olarak ayrıştırılıp ayrıştırılamayacağını kontrol etmek için; kullanımTryParse()
  • ancak nesne değil, bir sayı veya bir dize, ancak şüpheleniyorsanız ToString()o şey verebilir görünüyor bir numarası gibi, o zaman çağrı ToString() ve bir dize olarak ele

Her iki durumda da, muhtemelen desteklemek istediğiniz her sayısal türü ( double/ decimal/ int) ayrı ayrı ele almanız gerekecektir - örneğin, her birinin farklı aralıkları ve doğruluğu vardır.

Hızlı ve kaba bir kontrol için regex'e de bakabilirsiniz.


4

Girişinizin bir dize olduğunu varsayarsak ...

2 yol vardır:

Double. TryParse () kullanın

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

Normal ifade kullan

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

4

Kodu şu şekilde kullanabilirsiniz:

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

Nesne bir ise Int32, Single, Doublevb o dönüşümü gerçekleştirecektir. Ayrıca, bir dize uygular, IConvertibleancak dizge bir double'a dönüştürülemezse, o zaman a FormatExceptionatılır.


Aslında dizeler çözümlenir, ancak doğru biçimde değilse FormatException atılır.
alfoks

@alfoks: Kesinlikle haklısın, bu yüzden cevabı güncelledim.
Martin Liversage

1

Gereksiniminiz gerçekten ise

.ToString (), rakamlar ve +, -, içeren bir dizeyle sonuçlanır.

ve double. TryParse kullanmak istiyorsanız, bir NumberStyles parametresi alan aşırı yüklemeyi kullanmanız ve değişmez kültürü kullandığınızdan emin olmanız gerekir.

Örneğin, başında işareti olan, başında veya sonunda boşluk olmayan, binlik ayırıcı ve nokta ondalık ayırıcısı olmayan bir sayı için şunu kullanın:

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

1

object.IsNumeric()Saul Dolgin'in bu soruya verdiği cevaba dayanarak kendi uzatma yöntemimi yazarken veya OverflowExceptionile denerseniz bir alacağınız olası bir sorunla karşılaştım .double.MaxValuedouble.MinValue

Benim "çözümüm", Noldorin'den kabul edilen yanıtı Saul Dolgin'den gelen yanıtla birleştirmek ve herhangi bir şeyi ayrıştırmaya çalışmadan önce bir kalıp eşleştirme anahtarı eklemekti (ve biraz düzeltmek için biraz C # 7 iyiliği kullan):

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

Boş değer atanabilir türlere dikkat edin.
Guillermo Prandi

0

Evet, bu çalışıyor:

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

Bir kayan nokta numarası için kayan tip kullanarak test etmeniz gerekir:

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

Bu, nesne bir nesneye örtük veya açıkça atılmadan önce bir int ise işe yarar. Örneğinizde, 1 numaralı sihirli sayı bir inttir ve daha sonra x değişkeninin türüne implicity atılır. Eğer x = 1.0 nesnesini yapmış olsaydınız, iddianız yanlış döndürürdü.
Dexter

İnts olmayan sayılar var.
Fredrik Mörk

evet, bu yüzden benim açımdan şu anda @Noldorin'in cevabında ne var.
Peter Lillevold
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.