C # - cara menentukan apakah Jenis adalah angka

105

Apakah ada cara untuk menentukan apakah Jenis Net yang diberikan adalah angka atau tidak? Misalnya: System.UInt32/UInt16/Doublesemua angka. Saya ingin menghindari sakelar panjang di Type.FullName.

Adi Barda
sumber
4
Dupe dari banyak, banyak, banyak. Mengapa ini belum ditutup?
Noldorin
2
Duplikat stackoverflow.com/questions/1130698 dan sangat mirip dengan yang lain.
Henk Holterman

Jawaban:

110

Coba ini:

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

Jenis primitif adalah Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double, dan Single.

Mengambil solusi Guillaume sedikit lebih jauh:

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

Pemakaian:

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

string s = "Hello World";
s.IsNumericType(); // False
Philip Wallace
sumber
2
Jadi decimaljenisnya bukan numerik?
LukeH
2
@Xaero: Saya tidak ragu bahwa decimal adalah numerik. Hanya karena ini bukan primitif tidak berarti itu bukan numerik. Kode Anda perlu memperhitungkan ini.
LukeH
2
Ini perlu direkayasa ulang untuk tipe numerik baru di .NET 4.0 yang tidak memiliki kode tipe.
Jon Skeet
7
Bagaimana Anda bisa memberi saya suara rendah pada jawaban berdasarkan teknologi saat ini. Mungkin di .NET 62, int akan dihapus - apakah Anda akan merendahkan semua jawaban dengan int?
Philip Wallace
1
@DiskJunky Maaf, teman. Itu hampir tiga tahun lalu dan saya tidak ingat apa isinya.
kdbanman
93

Jangan gunakan sakelar - cukup gunakan satu set:

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

EDIT: Satu keuntungan dari ini dibandingkan menggunakan kode tipe adalah bahwa ketika tipe numerik baru diperkenalkan ke NET (misalnya BigInteger dan Kompleks ) mudah untuk menyesuaikan - sedangkan tipe tersebut tidak akan mendapatkan kode tipe.

Jon Skeet
sumber
4
dan bagaimana Anda akan menggunakan HashSet?
RvdK
8
NumericTypes.Contains (terserah)?
mqp
3
bool isANumber = NumericTypes.Contains (classInstance.GetType ());
Yuriy Faktorovich
Akan mengira kompilator akan melakukan konversi implisit dari pernyataan switch ke hashset.
Rolf Kristensen
6
@RolfKristensen: Sebenarnya switchtidak berhasil Type, jadi Anda tidak bisa. Anda TypeCodetentu saja dapat mengaktifkannya , tetapi itu masalah lain.
Jon Skeet
69

Tidak ada solusi yang memperhitungkan Nullable.

Saya sedikit memodifikasi solusi Jon Skeet:

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

Saya tahu saya bisa menambahkan nullables itu sendiri ke HashSet saya. Tetapi solusi ini menghindari bahaya lupa menambahkan Nullable tertentu ke daftar Anda.

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(int?),
        ...
    };
Jürgen Steinblock
sumber
2
Apakah tipe nullable benar-benar numerik? Null bukanlah angka, setahu saya.
IllidanS4 ingin Monica kembali
2
Itu tergantung pada apa yang ingin Anda capai. Dalam kasus saya, saya perlu menyertakan nullables juga. Tetapi saya juga dapat memikirkan situasi di mana ini bukan perilaku yang diinginkan.
Jürgen Steinblock
Baik! Untuk memperlakukan angka nullable sebagai angka sangat berguna dalam validasi input UI.
guogangj
1
@ IllidanS4 Ceknya ada di Type bukan nilainya. Dalam kebanyakan kasus, tipe numerik Nullable harus diperlakukan sebagai numerik. Tentu saja jika cek itu pada nilai dan nilainya null, maka ya itu tidak boleh dianggap numerik.
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;
  }
}

Catatan tentang pengoptimalan dihapus (lihat komentar enzi) Dan jika Anda benar-benar ingin mengoptimalkannya (kehilangan keterbacaan dan keamanan ...):

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

Guillaume
sumber
13
Saya tahu jawaban ini sudah lama, tetapi saya baru-baru ini menemukan peralihan seperti itu: jangan gunakan pengoptimalan yang disarankan! Saya melihat kode IL yang dihasilkan dari sakelar semacam itu, dan mencatat bahwa kompiler sudah menerapkan pengoptimalan (di IL 5 dikurangi dari kode jenis dan kemudian nilai dari 0 hingga 10 dianggap benar). Oleh karena itu, sakelar harus digunakan agar lebih mudah dibaca dan lebih aman serta sama cepatnya.
enzi
1
Jika Anda benar-benar ingin mengoptimalkannya dan tidak peduli tentang keterbacaan, kode yang optimal akan return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;menghapus cabang yang diperkenalkan oleh &&.
AnorZaken
14

Pada dasarnya solusi Skeet tetapi Anda dapat menggunakannya kembali dengan tipe Nullable sebagai berikut:

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);
    }
}
arviman
sumber
9

Pendekatan berdasarkan proposal Philip , ditingkatkan dengan pemeriksaan tipe dalam SFun28 untuk Nullabletipe:

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

Kenapa ini? Saya harus memeriksa apakah yang diberikan Type typeadalah tipe numerik, dan bukan jika sembarang object oadalah numerik.

cimnine
sumber
4

Dengan C # 7, metode ini memberi saya kinerja yang lebih baik daripada kasus sakelar TypeCodedan 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;

Tesnya adalah sebagai berikut:

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);
    }
Hugo Freitas
sumber
3

Anda dapat menggunakan Type.IsPrimitive dan kemudian mengurutkan Booleandan Chartype, seperti ini:

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

EDIT : Anda mungkin ingin mengecualikan IntPtrdan UIntPtrjenis juga, jika Anda tidak menganggapnya sebagai numerik.

Konamiman
sumber
1
Jadi decimaljenisnya bukan numerik?
LukeH
Ups ... yah, tampaknya solusi Guillaume adalah yang terbaik.
Konamiman
3

Ketik ekstensi dengan dukungan tipe null.

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;
        }
    }
Mateusz Kraska
sumber
1

Jawaban singkatnya: Tidak.

Jawaban yang Lebih Panjang: Tidak.

Faktanya adalah bahwa banyak tipe berbeda dalam C # dapat berisi data numerik. Kecuali Anda tahu apa yang diharapkan (Int, Double, dll), Anda perlu menggunakan pernyataan case "long".

Craig
sumber
1

Ini mungkin berhasil juga. Namun, Anda mungkin ingin menindaklanjutinya dengan Type.Parse untuk mentransmisikannya sesuai keinginan Anda sesudahnya.

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

Dimodifikasi skeet dan solusi arviman ini memanfaatkan Generics, Reflectiondan 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)
};

Diikuti oleh:

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

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

    return IsNumeric;
}

Penggunaan untuk (T item):

if ( item.IsNumeric() ) {}

null mengembalikan salah.

Rumah kucing
sumber
1

Switch agak lambat, karena setiap kali metode dalam situasi terburuk akan melalui semua jenis. Menurut saya, menggunakan Dictonary lebih bagus, dalam situasi ini Anda akan mendapatkan 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);
    }
}
Smagin Alexey
sumber
1

Coba paket nuget TypeSupport untuk C #. Ini memiliki dukungan untuk mendeteksi semua tipe numerik (di antara banyak fitur lainnya):

var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Michael Brown
sumber
Saya tidak tahu paket ini. Tampaknya menjadi penyelamat dalam banyak kasus untuk menghindari menulis kode kami sendiri untuk jenis operasi yang diminta oleh OP. Terima kasih!
AFakt
0

Sayangnya tipe ini tidak memiliki banyak kesamaan selain mereka semua tipe nilai. Tetapi untuk menghindari kasus-peralihan yang panjang Anda bisa mendefinisikan daftar hanya-baca dengan semua tipe ini dan kemudian hanya memeriksa apakah tipe yang diberikan ada di dalam daftar.

Darin Dimitrov
sumber
0

Mereka semua adalah tipe nilai (kecuali untuk bool dan mungkin enum). Jadi Anda cukup menggunakan:

bool IsNumberic(object o)
{
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
MandoMando
sumber
1
Ini akan mengembalikan true untuk setiap yang ditentukan pengguna struct... Saya tidak berpikir itu yang Anda inginkan.
Dan Tao
1
Anda benar. Tipe numerik built-in adalah struct juga. Jadi lebih baik gunakan perbandingan Primitif.
MandoMando
0

EDIT: Ya, saya memodifikasi kode di bawah ini menjadi lebih berkinerja dan kemudian menjalankan tes yang diposting oleh @Hugo terhadapnya. Kecepatannya hampir setara dengan IF @Hugo menggunakan item terakhir dalam urutannya (Desimal). Namun jika menggunakan item pertama 'byte' nya mengambil kue, tetapi jelas urutan penting dalam hal kinerja. Meskipun menggunakan kode di bawah ini lebih mudah untuk ditulis dan lebih konsisten pada biayanya, bagaimanapun, itu tidak dapat dipelihara atau dapat dibuktikan di masa depan.

Sepertinya beralih dari Type.GetTypeCode () ke Convert.GetTypeCode () mempercepat kinerja secara drastis, sekitar 25%, VS Enum.Parse () yang 10 kali lebih lambat.


Saya tahu posting ini sudah tua tetapi JIKA menggunakan metode enum TypeCode, termudah (dan mungkin yang termurah) akan menjadi seperti ini:

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

Diberikan definisi enum berikut untuk TypeCode:

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
}

Saya belum mengujinya secara menyeluruh, tetapi untuk tipe numerik C # dasar, ini sepertinya menutupinya. Namun, seperti yang disebutkan @JonSkeet, enum ini tidak diperbarui untuk jenis tambahan yang ditambahkan ke .NET di masa mendatang.

Hector Bas
sumber
-1

Ups! Salah membaca pertanyaan! Secara pribadi, akan berguling dengan Skeet's .


hrm, suara seperti Anda ingin DoSomethingpada Typedata Anda. Apa yang dapat Anda lakukan adalah sebagai berikut

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;
    }
}
johnny g
sumber