C# - how to determine whether a Type is a number

后端 未结 18 2213
傲寒
傲寒 2020-11-28 22:47

Is there a way to determine whether or not a given .Net Type is a number? For example: System.UInt32/UInt16/Double are all numbers. I want to avoid a long switc

相关标签:
18条回答
  • 2020-11-28 23:23

    With C# 7 this method gives to me better performance than switch case on TypeCode and 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;
    

    Tests are following:

    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);
        }
    
    0 讨论(0)
  • 2020-11-28 23:25

    Short answer: No.

    Longer Answer: Nope.

    The fact is that many different types in C# can contain numeric data. Unless you know what to expect (Int, Double, etc) you need to use the "long" case statement.

    0 讨论(0)
  • 2020-11-28 23:26

    Modified skeet's and arviman's solution utilizing Generics, Reflection, and 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)
    };
    

    Followed By:

    public static bool IsNumeric<T>( this T myType )
    {
        var IsNumeric = false;
    
        if( myType != null )
        {
            IsNumeric = m_numTypes.Contains( myType.GetType() );
        }
    
        return IsNumeric;
    }
    

    Usage for (T item):

    if ( item.IsNumeric() ) {}
    

    null returns false.

    0 讨论(0)
  • 2020-11-28 23:27

    oops! Misread the question! Personally, would roll with Skeet's.


    hrm, sounds like you want to DoSomething on Type of your data. What you could do is the following

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 23:28

    This may work as well. However, you may want to follow it up with a Type.Parse to cast it the way you want it afterwards.

    public bool IsNumeric(object value)
    {
        float testValue;
        return float.TryParse(value.ToString(), out testValue);
    }
    
    0 讨论(0)
  • 2020-11-28 23:30
    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;
      }
    }
    

    Note about optimization removed (see enzi comments) And if you really want to optimize it (losing readability and some safety...):

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

    0 讨论(0)
提交回复
热议问题