C#-如何确定类型是否为数字


Answers:


110

试试这个:

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

基本类型为布尔,字节,字节,Int16,UInt16,Int32,UInt32,Int64,UInt64,Char,Double和Single。

纪尧姆的解决方案远一点:

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
那么decimal类型不是数字吗?
LukeH

2
@Xaero:我毫无疑问decimal 数字。仅仅因为它不是基元并不意味着它不是数字。您的代码需要考虑到这一点。
LukeH

2
对于.NET 4.0中没有类型代码的新数字类型,需要对其进行重新设计。
乔恩·斯基特

7
您如何才能对基于当前技术的答案不满意。也许在.NET 62中,int将被删除-您是否要对所有带有int的答案进行投票?
菲利普·华莱士

1
@DiskJunky对不起,朋友。那差不多是三年前,我不记得他们的内容是什么。
kdbanman

93

不要使用开关-只需使用一个开关即可:

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

编辑:与使用类型代码相比,此方法的一个优点是,当将新的数字类型引入.NET(例如BigIntegerComplex)时,它很容易调整-而这些类型将不会获得类型代码。


4
以及如何使用HashSet?
RvdK

8
NumericTypes.Contains(whatever)?
mqp

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

本来以为编译器会将切换语句隐式转换为哈希集。
Rolf Kristensen

6
@RolfKristensen:好吧,这switch根本不起作用Type,所以你不能。您TypeCode当然可以打开,但这是另一回事。
乔恩·斯基特

69

所有解决方案都没有考虑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));
    }

我知道我可以将nullable本身添加到HashSet中。但是此解决方案避免了忘记将特定Nullable添加到列表中的危险。

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

2
可空类型真的是数字吗?据我所知,Null不是数字。
IllidanS4希望莫妮卡回到2014年

2
这取决于您要实现的目标。就我而言,我也需要包含可为空的内容。但是我也可以想到这种情况不是所期望的。
于尔根Steinblock

好!在UI输入验证中,将可空数字视为数字非常有用。
guogangj

1
@ IllidanS4在支票类型不是值。在大多数情况下,可为空的数字类型应视为数字。当然,如果检查的是值并且value为null,则是的,不应将其视为数字。
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;
  }
}

关于优化的注意事项已删除(请参阅enzi注释) ,如果您真的想对其进行优化(失去可读性和安全性...):

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
我知道这个答案很旧,但是最近我遇到了这样一个转换:不要使用建议的优化!我查看了从此类开关生成的IL代码,并注意到编译器已经应用了优化(在IL中从类型代码中减去5,然后将0到10的值视为true)。因此,应该使用该开关,因为它更易读,更安全且速度也很快。
enzi 2015年

1
如果您实际上要优化它,而不在乎可读性,那么最佳代码将return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;因此删除引入的分支&&
AnorZaken

14

基本上是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);
    }
}

9

根据接近菲利普的建议,具有增强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是否为数字。


4

使用C#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);
    }

3

您可以使用Type.IsPrimitive,然后对BooleanChar类型进行排序,如下所示:

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

编辑:如果您不认为IntPtrUIntPtr类型是数字,则可能也要排除和类型。


1
那么decimal类型不是数字吗?
LukeH

糟糕...看来,纪尧姆的解决方案毕竟是最好的。
Konamiman

3

类型扩展具有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;
        }
    }

1

简短答案:不可以。

更长的答案:不。

事实是C#中的许多不同类型都可以包含数字数据。除非您知道期望什么(Int,Double等),否则需要使用“ long” case语句。


1

这也可能起作用。但是,您可能需要使用Type.Parse进行后续处理,以将其转换为所需的方式。

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

1

修改飞碟双向的和arviman的解决方案使用GenericsReflectionC# 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。


1

切换有点慢,因为每次在最坏情况下的方法都会经过所有类型。我认为,使用Dictonary更好,在这种情况下,您将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#的TypeSupport nuget软件包。它支持检测所有数字类型(还有许多其他功能):

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

我不知道这个包裹。在许多情况下,它似乎是救生员,可以避免为OP所要求的操作编写自己的代码。谢谢 !
AFract

0

不幸的是,除了它们都是值类型之外,这些类型没有太多共同之处。但是要避免长时间切换,您可以只定义一个具有所有这些类型的只读列表,然后检查给定类型是否在列表内。


0

它们都是值类型(布尔值和枚举值除外)。因此,您可以简单地使用:

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

1
对于任何用户定义,此方法都将返回true struct...我认为这不是您想要的。
丹涛

1
你是对的。内置数字类型也是结构。所以最好先进行原始比较。
MandoMando

0

编辑:好吧,我修改了下面的代码以提高性能,然后对它运行@Hugo发布的测试。使用他序列中的最后一项(十进制),速度与@Hugo的IF差不多。但是,如果使用第一项“字节”,那么他就吃了蛋糕,但显然在性能方面顺序很重要。尽管使用下面的代码更容易编写并且在成本上更加一致,但是,它不是可维护的或未来可证明的。

看起来从Type.GetTypeCode()转换为Convert.GetTypeCode()大大提高了性能,比VS Enum.Parse()快25%,这要慢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
}

我还没有对它进行彻底的测试,但是对于基本的C#数字类型,这似乎涵盖了它。但是,正如@JonSkeet所提到的,此枚举不会针对添加到.NET中的其他类型进行更新。


-1

哎呀!误解了问题!就个人而言,将与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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.