Подтвердить что ты не робот

С# - как определить, является ли тип числом

Есть ли способ определить, является ли данный .Net Type числом? Например: System.UInt32/UInt16/Double - все числа. Я хочу избежать длинного переключателя на Type.FullName.

4b9b3361

Ответ 1

Попробуйте следующее:

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

Примитивные типы являются булевыми, байт, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double и Один.Забастовкa >

Взяв решение Guillaume немного дальше:

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

Использование:

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

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

Ответ 2

Не используйте переключатель - просто используйте набор:

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

EDIT: одно из преимуществ этого использования с использованием кода типа заключается в том, что когда новые типы данных вводятся в .NET(например, BigInteger и Complex) его легко настроить - в то время как эти типы не получат код типа.

Ответ 3

Ни одно из решений не учитывает Nullable.

Я немного изменил решение 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));
    }

Я знаю, что могу просто добавить собственные значения nullables в свой HashSet. Но это решение избегает опасности забыть добавить в свой список определенный Nullable.

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

Ответ 4

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

Примечание об удаленной оптимизации (см. комментарии enzi) <Удаp > И если вы действительно хотите его оптимизировать (потеря удобочитаемости и некоторая безопасность...):

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

Забастовкa >

Ответ 5

В основном решение Skeet, но вы можете повторно использовать его с помощью типов Nullable следующим образом:

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

Ответ 6

Подход на основе Предложения Филиппа, расширенный с помощью проверки внутреннего типа SFun28 для Nullable типов:

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

Почему это? Я должен был проверить, является ли данный Type type числовым типом, а не если произвольный object o является числовым.

Ответ 7

С С# 7 этот метод дает мне лучшую производительность, чем случай переключения на TypeCode и 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;

Следующие тесты:

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

Ответ 8

Вы можете использовать Type.IsPrimitive, а затем отсортировать типы Boolean и Char, что-то вроде этого:

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

EDIT. Вы можете также исключить типы IntPtr и UIntPtr, если вы не считаете их числовыми.

Ответ 9

Короткий ответ: Нет.

Дольше ответ: Нет.

Дело в том, что много разных типов в С# могут содержать числовые данные. Если вы не знаете, чего ожидать (Int, Double и т.д.), Вам нужно использовать оператор "long" case.

Ответ 10

Это может сработать. Тем не менее, вы можете захотеть следить за ним с помощью Type.Parse, чтобы отдать его так, как вы захотите.

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

Ответ 11

К сожалению, эти типы не имеют много общего, кроме всех типов значений. Но чтобы избежать длинного случая переключения, вы можете просто определить список только для чтения со всеми этими типами, а затем просто проверить, находится ли данный тип внутри списка.

Ответ 12

Это все типы значений (кроме bool и, возможно, перечисления). Поэтому вы можете просто использовать:

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

Ответ 13

Модифицированное решение skeet и arviman с использованием Generics, Reflection и 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)
};

Далее:

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

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

    return IsNumeric;
}

Использование (T item):

if ( item.IsNumeric() ) {}

null возвращает false.

Ответ 14

РЕДАКТИРОВАТЬ: Ну, я изменил код ниже, чтобы быть более производительным, а затем провел тесты, опубликованные @Hugo против него. Скорости примерно такие же, как у @Hugo IF, если использовать последний элемент в его последовательности (десятичное число). Однако, если использовать первый элемент "байт", он забирает торт, но порядок имеет значение, когда дело доходит до производительности. Хотя использование приведенного ниже кода проще в написании и более согласовано с его стоимостью, оно, тем не менее, не подлежит обслуживанию и не подлежит проверке в будущем.

Похоже, что переключение с Type.GetTypeCode() на Convert.GetTypeCode() резко увеличило производительность, примерно на 25%, VS Enum.Parse(), которая была примерно в 10 раз медленнее.


Я знаю, что этот пост старый, но если использовать метод перечисления TypeCode, самый простой (и, вероятно, самый дешевый) будет что-то вроде этого:

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

Дано следующее определение перечисления для 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
}

Я не проверил это полностью, но для базовых числовых типов С# это, кажется, покрывает это. Однако, как упомянул @JonSkeet, это перечисление не обновляется для дополнительных типов, добавляемых в .NET в будущем.

Ответ 15

упс! Не обращай внимания на вопрос! Лично скачал бы с Skeet.


hrm, кажется, что вы хотите DoSomething на Type ваших данных. Что вы можете сделать, это следующее

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