[英]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.