[英]C# - how to determine whether a Type is a number
有没有办法确定给定的.Net Type 是否是数字? 例如: System.UInt32/UInt16/Double
都是数字。 我想避免在Type.FullName
上使用较长的 switch-case。
尝试这个:
Type type = object.GetType(); bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
基本类型是布尔、字节、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;
}
}
用法:
int i = 32;
i.IsNumericType(); // True
string s = "Hello World";
s.IsNumericType(); // False
不要使用开关 - 只需使用一组:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
编辑:与使用类型代码相比,这样做的一个优点是,当将新的数字类型引入 .NET(例如BigInteger和Complex )时,它很容易调整 - 而这些类型不会获得类型代码。
没有一个解决方案考虑到 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?),
...
};
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; }
基本上是 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(this Type myType)
{
return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
}
}
用法示例:
//static invocation
int someNumber = 5;
TypeHelper.IsNumeric(typeof(someNumber)); //true
string someText = "test";
TypeHelper.IsNumeric(typeof(someText)); //false
//invoke via extension method
typeof(decimal).IsNumeric(); // true
typeof(string).IsNumeric(); // false
基于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;
}
}
}
为什么这个? 我必须检查给定的Type type
是否为数字类型,而不是任意object o
是否为数字类型。
在 C# 7 中,这种方法比TypeCode
和HashSet<Type>
上的 switch case 提供了更好的性能:
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);
}
具有空类型支持的类型扩展。
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;
}
}
使用Generics
和C# v7.0
修改了skeet和arviman 的解决方案。
编辑:很多个月后回来以提高代码质量。
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;
}
示例/单元测试 注意 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());
}
}
基准/性能
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();
}
}
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
方法 | 意思是 | 错误 | 标准差 | 比率 | 比率标准差 | 0代 | 第一代 | 第 2 代 | 已分配 |
---|---|---|---|---|---|---|---|---|---|
是数字的 | 16.65 纳秒 | 0.104 纳秒 | 0.087 纳秒 | 1.00 | 0.00 | - | - | - | - |
IsNumericAtRuntime | 19.26 纳秒 | 0.409 纳秒 | 0.383 纳秒 | 1.16 | 0.02 | 0.0038 | - | - | 24乙 |
IsNullableNumeric | 58.65 纳秒 | 0.692 纳秒 | 0.647 纳秒 | 3.53 | 0.04 | 0.0038 | - | - | 24乙 |
尝试 C# 的TypeSupport nuget 包。 它支持检测所有数字类型(以及许多其他功能):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
您可以使用Type.IsPrimitive然后整理出Boolean
和Char
类型,如下所示:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
编辑:如果您不认为它们是数字的,您可能还想排除IntPtr
和UIntPtr
类型。
bool IsNumeric(Type type)
=> type == typeof(decimal)
|| type == typeof(int)
|| type == typeof(uint)
|| type == typeof(long)
|| type == typeof(ulong)
|| type == typeof(short)
|| type == typeof(ushort)
|| type == typeof(byte)
|| type == typeof(sbyte)
|| type == typeof(float)
|| type == typeof(double)
;
有没有一种方法可以确定给定的.Net类型是否为数字? 例如: System.UInt32/UInt16/Double
都是数字。 我想避免在Type.FullName
上使用长开关。
切换有点慢,因为每次方法在最坏的情况下都会遍历所有类型。 我认为,使用 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);
}
}
只是添加到正在查看TypeCode
的其他答案 - 如果您愿意,您可以进一步简化,以避免冗长的 switch 语句:
public static bool IsNumeric(this Type type)
{
var typeCode = (int)Type.GetTypeCode(type);
return typeCode > 4 && typeCode < 16;
}
public static bool IsNumeric(this object source)
{
return source.GetType().IsNumeric();
}
这也可能有效。 但是,您可能希望使用 Type.Parse 跟进它,以便之后按照您想要的方式进行转换。
public bool IsNumeric(object value)
{
float testValue;
return float.TryParse(value.ToString(), out testValue);
}
编辑:好吧,我修改了下面的代码以提高性能,然后针对它运行@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
}
我还没有彻底测试它,但对于基本的 C# 数字类型,这似乎涵盖了它。 但是,正如@JonSkeet 所提到的,此枚举不会针对将来添加到 .NET 的其他类型进行更新。
.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));
}
当前在 .Net 7 预览版中实现此功能的类型是:
byte
char
decimal
double
Half
short
int
long
Int128
nint
BigInteger
Complex
NFloat
sbyte
float
ushort
uint
ulong
UInt128
nuint
随着 C#11 和 Dotnet 7 的出现,我们有了通用数学!
您可以检查一个类型是否实现了INumber<T>
,此时您可以对数字执行您希望能够执行的所有操作。
为此,您可以使用typeof(type).isAssignableTo(typeof(INumber<>))
。
例如,要检索所有框架提供的数字类型,您可以运行:
List<Type> numericTypes = typeof(Type).Assembly.GetTypes()
.Where(t => t.IsAssignableTo(typeof(INumber<>)))
.ToList();
基于codybartfast answer using .NET7 and System.Numerics,实际上,这种扩展方法可以解决
public static bool IsNumeric(this Type type)
{
var numType = typeof(INumber<>);
var result = type.GetInterfaces().Any(i => i.IsGenericType && (i.GetGenericTypeDefinition() == numType));
return result;
}
不幸的是,除了它们都是值类型之外,这些类型没有太多共同点。 但是为了避免长开关情况,您可以只定义一个包含所有这些类型的只读列表,然后检查给定类型是否在列表内。
它们都是值类型(bool 和枚举除外)。 所以你可以简单地使用:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
哎呀! 看错问题了! 就个人而言,会与Skeet's一起滚动。
人力资源管理, 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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.