C # - Türün sayı olup olmadığını belirleme


105

Belirli bir .Net Türünün bir sayı olup olmadığını belirlemenin bir yolu var mı? Örneğin: System.UInt32/UInt16/Doublehepsi sayıdır. Üzerinde uzun bir geçiş durumundan kaçınmak istiyorum Type.FullName.


4
Çok, çok, çok sayıda kopya. Bu neden henüz kapatılmadı?
Noldorin

2
Kopyası stackoverflow.com/questions/1130698 ve çok yakın bazı diğerlerine.
Henk Holterman

Yanıtlar:


110

Bunu dene:

Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));

İlkel türler Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double ve Single'dır.

Alarak Guillaume'nin çözümü biraz daha:

public static bool IsNumericType(this object o)
{   
  switch (Type.GetTypeCode(o.GetType()))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

Kullanım:

int i = 32;
i.IsNumericType(); // True

string s = "Hello World";
s.IsNumericType(); // False

2
Yani decimaltip sayısal değil mi?
LukeH

2
@Xaero: Hiç şüphem yok decimal olduğunu sayısal. İlkel olmaması, sayısal olmadığı anlamına gelmez. Kodunuzun bunu hesaba katması gerekir.
LukeH

2
NET 4.0'da tür kodları olmayan yeni sayısal türler için bunun yeniden tasarlanması gerekir.
Jon Skeet

7
Mevcut teknolojiye dayalı bir yanıta bana nasıl olumsuz oy verebilirsiniz? Belki .NET 62'de int kaldırılacaktır - tüm yanıtları int ile aşağı oylayacak mısınız?
Philip Wallace

1
@DiskJunky Üzgünüm, arkadaş. Bu neredeyse üç yıl önceydi ve içeriklerinin ne olduğunu hatırlamıyorum.
kdbanman

93

Anahtar kullanmayın - sadece bir set kullanın:

HashSet<Type> NumericTypes = new HashSet<Type>
{
    typeof(decimal), typeof(byte), typeof(sbyte),
    typeof(short), typeof(ushort), ...
};

DÜZENLEME: Bunun bir tür kodu kullanmaya göre bir avantajı, .NET'e yeni sayısal türler eklendiğinde (ör. BigInteger ve Karmaşık ) ayarlamanın kolay olmasıdır - oysa bu türler bir tür kodu almaz .


4
ve HashSet'i nasıl kullanırsınız?
RvdK

8
NumericTypes.Contains (ne olursa olsun)?
mqp

3
bool isANumber = NumericTypes.Contains (classInstance.GetType ());
Yuriy Faktorovich

Derleyicinin anahtar ifadesini hashset'e örtük bir dönüşümü yapacağını düşünürdüm.
Rolf Kristensen

6
@RolfKristensen: switchSadece üzerinde çalışmıyor Type, bu yüzden yapamazsınız. TypeCodeElbette açabilirsiniz , ancak bu farklı bir konu.
Jon Skeet

69

Çözümlerin hiçbiri Nullable'ı hesaba katmaz.

Jon Skeet'in çözümünü biraz değiştirdim:

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(uint),
        typeof(double),
        typeof(decimal),
        ...
    };

    internal static bool IsNumericType(Type type)
    {
        return NumericTypes.Contains(type) ||
               NumericTypes.Contains(Nullable.GetUnderlyingType(type));
    }

Boş değerlerin kendisini HashSet'ime ekleyebileceğimi biliyorum. Ancak bu çözüm, listenize belirli bir Nullable eklemeyi unutma tehlikesini ortadan kaldırır.

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(int?),
        ...
    };

2
Boş değer atanabilir bir tür gerçekten sayısal mı? Bildiğim kadarıyla boş bir sayı değil.
IllidanS4, Monica'yı

2
Bu, neyi başarmak istediğinize bağlıdır. Benim durumumda, boş değerleri de eklemem gerekiyordu. Ama bunun istenen bir davranış olmadığı durumları da düşünebilirim.
Jürgen Steinblock

İyi! Null yapılabilir sayıyı sayı olarak değerlendirmek, UI giriş doğrulamasında çok kullanışlıdır.
guogangj

1
IllidanS4 @ onay üzerindedir Tipi değil değer. Çoğu durumda Null yapılabilir sayısal türler sayısal olarak ele alınmalıdır. Tabii ki kontrol değer üzerindeyse ve değer boşsa, o zaman evet sayısal olarak kabul edilmemelidir.
nawfal

40
public static bool IsNumericType(Type type)
{
  switch (Type.GetTypeCode(type))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

Kaldırılan optimizasyon hakkında not (enzi yorumlarına bakın) Ve gerçekten optimize etmek istiyorsanız (okunabilirliği kaybetmek ve biraz güvenlik ...):

public static bool IsNumericType(Type type)
{
  TypeCode typeCode = Type.GetTypeCode(type);
  //The TypeCode of numerical types are between SByte (5) and Decimal (15).
  return (int)typeCode >= 5 && (int)typeCode <= 15;
}


13
Bu cevabın eski olduğunu biliyorum, ancak yakın zamanda böyle bir anahtarla karşılaştım: önerilen optimizasyonu kullanmayın! Böyle bir anahtardan üretilen IL koduna baktım ve derleyicinin optimizasyonu zaten uyguladığını fark ettim (IL 5'te tür kodundan çıkarılır ve ardından 0'dan 10'a kadar değerler doğru kabul edilir). Bu nedenle, anahtar daha okunabilir, daha güvenli ve aynı hızda kullanılmalıdır.
enzi

1
Eğer onu gerçekten optimize etmek istiyorsanız ve okunabilirliği umursamıyorsanız, en uygun kod, bu return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;nedenle tarafından tanıtılan dalı kaldırmak olacaktır &&.
AnorZaken

14

Temelde Skeet'in çözümü, ancak bunu Nullable türlerle aşağıdaki gibi yeniden kullanabilirsiniz:

public static class TypeHelper
{
    private static readonly HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),  typeof(double),  typeof(decimal),
        typeof(long), typeof(short),   typeof(sbyte),
        typeof(byte), typeof(ulong),   typeof(ushort),  
        typeof(uint), typeof(float)
    };

    public static bool IsNumeric(Type myType)
    {
       return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
    }
}

9

Philip'in önerisine dayanan yaklaşım , SFun28'inNullable türler için dahili tip kontrolü ile geliştirilmiş :

public static class IsNumericType
{
    public static bool IsNumeric(this Type type)
    {
        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            case TypeCode.Object:
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    return Nullable.GetUnderlyingType(type).IsNumeric();
                    //return IsNumeric(Nullable.GetUnderlyingType(type));
                }
                return false;
            default:
                return false;
        }
    }
}

Neden bu? Type typeVerilenin sayısal bir tür olup olmadığını kontrol etmem gerekiyordu , keyfi object obir sayısal mı?


4

C # 7 ile bu yöntem bana durumu açmaktan daha iyi performans veriyor TypeCodeve HashSet<Type>:

public static bool IsNumeric(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is float || o is double || o is decimal;

Testler şu şekildedir:

public static class Extensions
{
    public static HashSet<Type> NumericTypes = new HashSet<Type>()
    {
        typeof(byte), typeof(sbyte), typeof(ushort), typeof(uint), typeof(ulong), typeof(short), typeof(int), typeof(long), typeof(decimal), typeof(double), typeof(float)
    };

    public static bool IsNumeric1(this object o) => NumericTypes.Contains(o.GetType());

    public static bool IsNumeric2(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is decimal || o is double || o is float;

    public static bool IsNumeric3(this object o)
    {
        switch (o)
        {
            case Byte b:
            case SByte sb:
            case UInt16 u16:
            case UInt32 u32:
            case UInt64 u64:
            case Int16 i16:
            case Int32 i32:
            case Int64 i64:
            case Decimal m:
            case Double d:
            case Single f:
                return true;
            default:
                return false;
        }
    }

    public static bool IsNumeric4(this object o)
    {
        switch (Type.GetTypeCode(o.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            default:
                return false;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {           
        var count = 100000000;

        //warm up calls
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }

        //Tests begin here
        var sw = new Stopwatch();
        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);
    }

3

Type.IsPrimitive'i kullanabilir ve ardından Booleanve Chartürlerini şu şekilde sıralayabilirsiniz :

bool IsNumeric(Type type)
{
    return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}

DÜZENLEME : Sayısal olmadıklarını düşünmüyorsanız IntPtrve UIntPtrtürlerini de hariç tutmak isteyebilirsiniz .


1
Yani decimaltip sayısal değil mi?
LukeH

Ooops ... Pekala, öyle görünüyor ki Guillaume'un çözümü sonuçta en iyisi.
Konamiman

3

Boş tip destekli uzantı yazın.

public static bool IsNumeric(this Type type)
    {
        if (type == null) { return false; }

        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            type = type.GetGenericArguments()[0];
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            default:
                return false;
        }
    }

1

Kısa cevap: Hayır.

Daha Uzun Cevap: Hayır.

Gerçek şu ki, C #'daki birçok farklı tür sayısal veriler içerebilir. Ne bekleyeceğinizi bilmiyorsanız (Int, Double, vb.) "Long" case ifadesini kullanmanız gerekir.


1

Bu da işe yarayabilir. Ancak, daha sonra istediğiniz şekilde yayınlamak için bir Type.Parse ile takip etmek isteyebilirsiniz.

public bool IsNumeric(object value)
{
    float testValue;
    return float.TryParse(value.ToString(), out testValue);
}

1

Modifiye skeet en ve arviman çözümü kullanan Generics, Reflectionve C# v6.0.

private static readonly HashSet<Type> m_numTypes = new HashSet<Type>
{
    typeof(int),  typeof(double),  typeof(decimal),
    typeof(long), typeof(short),   typeof(sbyte),
    typeof(byte), typeof(ulong),   typeof(ushort),
    typeof(uint), typeof(float),   typeof(BigInteger)
};

Bunu takiben:

public static bool IsNumeric<T>( this T myType )
{
    var IsNumeric = false;

    if( myType != null )
    {
        IsNumeric = m_numTypes.Contains( myType.GetType() );
    }

    return IsNumeric;
}

Kullanım için (T item):

if ( item.IsNumeric() ) {}

null yanlış döndürür.


1

Geçiş biraz yavaştır, çünkü her seferinde en kötü durumdaki yöntemler her türden geçecektir. Bence, Dictonary kullanmak daha güzel, bu durumda sahip olacaksınız O(1):

public static class TypeExtensions
{
    private static readonly HashSet<Type> NumberTypes = new HashSet<Type>();

    static TypeExtensions()
    {
        NumberTypes.Add(typeof(byte));
        NumberTypes.Add(typeof(decimal));
        NumberTypes.Add(typeof(double));
        NumberTypes.Add(typeof(float));
        NumberTypes.Add(typeof(int));
        NumberTypes.Add(typeof(long));
        NumberTypes.Add(typeof(sbyte));
        NumberTypes.Add(typeof(short));
        NumberTypes.Add(typeof(uint));
        NumberTypes.Add(typeof(ulong));
        NumberTypes.Add(typeof(ushort));
    }

    public static bool IsNumber(this Type type)
    {
        return NumberTypes.Contains(type);
    }
}

1

C # için TypeSupport nuget paketini deneyin . Tüm sayısal türleri tespit etme desteği vardır (diğer birçok özelliğin yanı sıra):

var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);

Bu paketi bilmiyordum. OP tarafından talep edilen türden işlemler için kendi kodumuzu yazmaktan kaçınmak birçok durumda hayat kurtarıcı gibi görünüyor. Teşekkürler !
06'da çekin

0

Maalesef, bu türlerin hepsinin değer türleri dışında çok fazla ortak yanı yoktur. Ancak uzun bir geçiş durumunu önlemek için tüm bu türlerle salt okunur bir liste tanımlayabilir ve ardından verilen türün listenin içinde olup olmadığını kontrol edebilirsiniz.


0

Bunların hepsi değer türleridir (bool ve belki enum hariç). Yani şunları kullanabilirsiniz:

bool IsNumberic(object o)
{
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}

1
Bu, herhangi bir kullanıcı tanımlı için doğru dönecektir struct... İstediğinizin bu olduğunu sanmıyorum.
Dan Tao

1
Haklısın. Yerleşik sayısal türler de yapılardır. Öyleyse İlkel karşılaştırmaya gitsek iyi olur.
MandoMando

0

DÜZENLEME: Aşağıdaki kodu daha performanslı olması için değiştirdim ve ardından @Hugo tarafından gönderilen testleri buna karşı yürüttüm. Hızlar, dizisindeki son öğeyi (Ondalık) kullanan @ Hugo'nun IF ile hemen hemen aynı. Bununla birlikte, ilk öğe 'bayt'ı kullanıyorsa, pastayı alır, ancak performans söz konusu olduğunda sipariş açıkça önemlidir. Aşağıdaki kodu kullanmak daha kolay ve maliyeti açısından daha tutarlı olsa da, yine de sürdürülemez veya gelecekte kanıtlanabilir değildir.

Görünüşe göre Type.GetTypeCode () 'den Convert.GetTypeCode ()' a geçiş, performansı önemli ölçüde hızlandırdı, yaklaşık% 25, ​​VS Enum.Parse (), bu 10 kat daha yavaş gibiydi.


Bu yazı eski olduğunu biliyorum ama IF TypeCode enum yöntemi kullanılarak, en kolay (ve muhtemelen en ucuz) böyle bir şey olacaktır:

public static bool IsNumericType(this object o)
{   
  var t = (byte)Convert.GetTypeCode(o);
  return t > 4 && t < 16;
}

TypeCode için aşağıdaki enum tanımı verildiğinde:

public enum TypeCode
{
    Empty = 0,
    Object = 1,
    DBNull = 2,
    Boolean = 3,
    Char = 4,
    SByte = 5,
    Byte = 6,
    Int16 = 7,
    UInt16 = 8,
    Int32 = 9,
    UInt32 = 10,
    Int64 = 11,
    UInt64 = 12,
    Single = 13,
    Double = 14,
    Decimal = 15,
    DateTime = 16,
    String = 18
}

Tam olarak test etmedim, ancak temel C # sayısal türleri için bu onu kapsıyor gibi görünüyor. Ancak, @JonSkeet'in de belirttiği gibi, bu numaralandırma yolda .NET'e eklenen ek türler için güncellenmez.


-1

oops! Soruyu yanlış okuyun! Şahsen, Skeet's ile yuvarlanırdı .


hrm, senin gibi sesler istiyoruz DoSomethingüzerindeki Typeverilerin. Yapabileceğin şey şudur

public class MyClass
{
    private readonly Dictionary<Type, Func<SomeResult, object>> _map = 
        new Dictionary<Type, Func<SomeResult, object>> ();

    public MyClass ()
    {
        _map.Add (typeof (int), o => return SomeTypeSafeMethod ((int)(o)));
    }

    public SomeResult DoSomething<T>(T numericValue)
    {
        Type valueType = typeof (T);
        if (!_map.Contains (valueType))
        {
            throw new NotSupportedException (
                string.Format (
                "Does not support Type [{0}].", valueType.Name));
        }
        SomeResult result = _map[valueType] (numericValue);
        return result;
    }
}
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.