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

Adi*_*rda 94 .net c# types

有没有办法确定给定的.Net类型是否为数字?例如:System.UInt32/UInt16/Double都是数字.我想避免使用长开关盒Type.FullName.

Phi*_*ace 98

试试这个:

Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Run Code Online (Sandbox Code Playgroud)

基元类型是布尔,字节,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Char,Double和Single.

再采取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;
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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

string s = "Hello World";
s.IsNumericType(); // False
Run Code Online (Sandbox Code Playgroud)

  • 你如何根据现有技术给我一个答案.也许在.NET 62中,int将被删除 - 你是否会使用int downvote所有答案? (6认同)
  • 那么`decimal`类型不是数字? (2认同)
  • @Xaero:我毫不怀疑`decimal`*是*数字.仅仅因为它不是原始的并不意味着它不是数字.您的代码需要考虑到这一点. (2认同)
  • 对于没有类型代码的.NET 4.0中的新数字类型,需要对其进行重新设计。 (2认同)
  • 如果您想使用此方法检查可空类型是否为“数字”,您可以使用: `var type = o.GetType().GetEnumUnderlyingType() ?? o.GetType();` 然后打开类型:`switch (Type.GetTypeCode(type))` (2认同)

Jon*_*eet 86

不要使用开关 - 只需使用一套:

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

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

  • NumericTypes.Contains(什么)? (8认同)
  • 以及如何使用HashSet? (4认同)
  • @RolfKristensen:好吧`switch`根本不适用于`Type`,所以你做不到.你当然可以打开`TypeCode`,但这是另一回事. (4认同)
  • bool isANumber = NumericTypes.Contains(classInstance.GetType()); (2认同)

Jür*_*ock 65

没有任何解决方案考虑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));
    }
Run Code Online (Sandbox Code Playgroud)

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

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(int?),
        ...
    };
Run Code Online (Sandbox Code Playgroud)

  • 可空类型真的是数字吗?据我所知,Null不是数字。 (2认同)
  • 这取决于您要实现的目标。就我而言,我也需要包含可为空的内容。但是我也可以想到这种情况不是所期望的。 (2认同)

Gui*_*ume 36

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

关于优化的注意事项已删除(参见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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我知道这个答案很老,但我最近遇到了这样的转换:不要使用建议的优化!我查看了从这样的开关生成的IL代码,并注意到编译器已经应用了优化(在IL 5中从类型代码中减去,然后从0到10的值被认为是真的).因此,应该使用开关,因为它更可读,更安全,同样快. (13认同)
  • 如果您确实想优化它并且不关心可读性,则最佳代码将是“return unchecked((uint)Type.GetTypeCode(type) - 5u) &lt;= 10u;”,从而删除“&amp;&amp;”引入的分支。 (2认同)

arv*_*man 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


cim*_*ine 9

基于Philip的提议的方法,通过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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么这个?我必须检查给定Type type是否是数字类型,而不是任意object o数字.


Mat*_*ska 6

具有空类型支持的类型扩展。

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


Hou*_*Cat 5

利用、 和修改了skeetarviman 的解决方案。GenericsC# v7.0

编辑:很多个月后回来以提高代码质量。

using System;
using System.Collections.Generic;
using System.Numerics;

public static class GenericTypeExtensions
{
    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), typeof(BigInteger)
    };

    public static bool IsNumeric<T>(this T input)
    {
        if (input is null) return false;

        return _numericTypes.Contains(typeof(T));
    }

    public static bool IsNumericAtRuntime<T>(this T input)
    {
        if (input is null) return false;

        return _numericTypes.Contains(input.GetType());
    }

    /// <summary>
    /// Identifies whether or not this object is a numeric or nullable numeric type.
    /// <para>Examples</para>
    /// <para />int value = 0; true
    /// <para />var objValue = (object)(int)0; true
    /// <para />int? value = 0; true
    /// <para />int? value = null; true
    /// <para />var objValue = (object)(int?)0; true
    /// <para />var objValue = (object)(int?)(null); false - because (int?) is totally lost.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="input"></param>
    /// <returns></returns>
    public static bool IsNullableNumeric<T>(this T input)
    {
        if (input is null)
        {
            return _numericTypes.Contains(Nullable.GetUnderlyingType(typeof(T))); // see what the inner base type is
        }

        return _numericTypes.Contains(input.GetType());
    }

    public static void AddCustomNumericType<T>(this T _) where T : IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
    {
        _numericTypes.Add(typeof(T));
    }

    public static bool TryAddCustomNumeric<T>(T input)
    {
        Type type;
        if (input is null)
        {
            type = Nullable.GetUnderlyingType(typeof(T));
            if (type is null) return false;
        }
        else
        { type = input.GetType(); }

        if (_numericTypes.Contains(type)) return true;

        var interfaces = type.GetInterfaces();
        var count = 0;

        for (var i = 0; i < interfaces.Length; i++)
        {
            switch(interfaces[i])
            {
                case IComparable:
                case IComparable<T>:
                case IConvertible:
                case IEquatable<T>:
                case IFormattable:
                    count++;
                    break;
                default: continue;
            }
        }

        if (count != 5) return false;

        _numericTypes.Add(type);
        return true;
    }

    public static bool TryAddCustomNumericType<T>(Type type)
    {
        if (type is null) return false;

        if (_numericTypes.Contains(type)) return true;

        var interfaces = type.GetInterfaces();
        var count = 0;

        for (var i = 0; i < interfaces.Length; i++)
        {
            switch (interfaces[i])
            {
                case IComparable:
                case IComparable<T>:
                case IConvertible:
                case IEquatable<T>:
                case IFormattable:
                    count++;
                    break;
                default: continue;
            }
        }

        if (count != 5) return false;

        _numericTypes.Add(type);
        return true;
    }
Run Code Online (Sandbox Code Playgroud)

示例/单元测试 请注意 Assert.True/False 翻转。

public class IsNumericTests
{
    [Fact]
    public void IsNumeric()
    {
        var value = 0;

        Assert.True(value.IsNumeric());
    }

    [Fact]
    public void IsObjectNumeric()
    {
        var value = 0;
        var objValue = (object)value;

        Assert.False(objValue.IsNumeric());
    }

    [Fact]
    public void IsNumericAtRuntime()
    {
        var value = 0;

        Assert.True(value.IsNumericAtRuntime());
    }

    [Fact]
    public void IsObjectNumericAtRuntime()
    {
        var value = 0;
        var objValue = (object)value;

        Assert.True(objValue.IsNumericAtRuntime());
    }

    [Fact]
    public void IsNullableNumeric()
    {
        int? value = 0;

        Assert.True(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericAsObject()
    {
        int? value = 0;
        var objValue = (object)value;

        Assert.True(objValue.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNull()
    {
        int? value = null;

        Assert.True(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNullAsObject()
    {
        int? value = null;
        var objValue = (object)value;

        Assert.False(objValue.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNotNumber()
    {
        string value = "test";

        Assert.False(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNullAndNotNumber()
    {
        Type? value = null;

        Assert.False(value.IsNullableNumeric());
    }
}
Run Code Online (Sandbox Code Playgroud)

基准/性能

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using HouseofCat.Extensions;
using System;

[MarkdownExporterAttribute.GitHub]
[MemoryDiagnoser]
[SimpleJob(runtimeMoniker: RuntimeMoniker.Net50 | RuntimeMoniker.NetCoreApp31)]
public class IsNumericBenchmark
{
    public int IntValue = 1;
    public long? LongValue = int.MaxValue;

    public object ObjIntValue => (object)IntValue;
    public object ObjLongValue => (object)LongValue;

    [Benchmark(Baseline = true)]
    public void IsNumeric()
    {
        IntValue.IsNumeric();
    }

    [Benchmark]
    public void IsNumericAtRuntime()
    {
        ObjIntValue.IsNumericAtRuntime();
    }

    [Benchmark]
    public void IsNullableNumeric()
    {
        LongValue.IsNullableNumeric();
    }
}
Run Code Online (Sandbox Code Playgroud)

BenchmarkDotNet=v0.13.0, OS=Windows 10.0.18363.1621 (1909/November2019Update/19H2)
Intel Core i7-9850H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=5.0.400-preview.21277.10
  [Host]   : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET 5.0 : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT

Job=.NET 5.0  Runtime=.NET 5.0  

Run Code Online (Sandbox Code Playgroud)
方法 意思是 错误 标准差 比率 比率SD 第0代 第一代 第2代 已分配
是数字 16.65纳秒 0.104纳秒 0.087纳秒 1.00 0.00 - - - -
运行时为数值 19.26纳秒 0.409纳秒 0.383纳秒 1.16 0.02 0.0038 - - 24乙
是否可为空数值 58.65纳秒 0.692纳秒 0.647纳秒 3.53 0.04 0.0038 - - 24乙

HouseofCat/Tesseract Github 存储库和 Nuget


Hug*_*tas 5

在 C# 7 中,这种方法比 switch case on 提供了更好的性能,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;
Run Code Online (Sandbox Code Playgroud)

测试如下:

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);
    }
Run Code Online (Sandbox Code Playgroud)


cod*_*ast 5

.NET 7(撰写本文时为预览版 5)将引入 INumeric<> 将由 20 个内置类型实现的接口。

当添加其他数字类型时,检查此接口可能是面向未来的。

static bool IsNumeric(object o){
    var numType = typeof(INumber<>);
    return o.GetType().GetInterfaces().Any(iface =>
        iface.IsGenericType && (iface.GetGenericTypeDefinition() == numType));
}
Run Code Online (Sandbox Code Playgroud)

目前在 .Net 7 预览版中实现此功能的类型有:

byte
char
decimal
double
Half
short
int
long
Int128
nint
BigInteger
Complex
NFloat
sbyte
float
ushort
uint
ulong
UInt128
nuint
Run Code Online (Sandbox Code Playgroud)