Có cách nào để xác định xem .Net Type đã cho có phải là một số hay không? Ví dụ: System.UInt32/UInt16/Double
đều là số. Tôi muốn tránh trường hợp chuyển đổi dài trên Type.FullName
.
Có cách nào để xác định xem .Net Type đã cho có phải là một số hay không? Ví dụ: System.UInt32/UInt16/Double
đều là số. Tôi muốn tránh trường hợp chuyển đổi dài trên Type.FullName
.
Câu trả lời:
Thử cái này:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Các kiểu nguyên thủy là Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double và Single.
Xem xét giải pháp của Guillaume xa hơn một chút:
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;
}
}
Sử dụng:
int i = 32;
i.IsNumericType(); // True
string s = "Hello World";
s.IsNumericType(); // False
decimal
loại không phải là số?
decimal
là số. Chỉ vì nó không phải là một nguyên thủy không có nghĩa là nó không phải là số. Mã của bạn cần phải giải thích cho điều này.
Không sử dụng công tắc - chỉ sử dụng một bộ:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
CHỈNH SỬA: Một ưu điểm của điều này so với việc sử dụng mã kiểu là khi các kiểu số mới được đưa vào .NET (ví dụ: BigInteger và Complex ), bạn sẽ dễ dàng điều chỉnh - trong khi những kiểu đó sẽ không nhận được mã kiểu.
switch
Đơn giản là nó không hoạt động Type
, vì vậy bạn không thể. TypeCode
Tất nhiên, bạn có thể bật , nhưng đó là một vấn đề khác.
Không có giải pháp nào tính đến Nullable.
Tôi đã sửa đổi giải pháp của Jon Skeet một chút:
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));
}
Tôi biết tôi chỉ có thể thêm chính các nullables vào HashSet của mình. Nhưng giải pháp này tránh nguy cơ quên thêm một Nullable cụ thể vào danh sách của bạn.
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;
}
}
Lưu ý về việc tối ưu hóa đã bị xóa (xem phần nhận xét trên enzi)
Và nếu bạn thực sự muốn tối ưu hóa nó (mất khả năng đọc và một số an toàn ...):
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;
}
return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;
do đó loại bỏ nhánh được giới thiệu bởi &&
.
Về cơ bản giải pháp của Skeet nhưng bạn có thể sử dụng lại nó với các kiểu Nullable như sau:
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);
}
}
Phương pháp tiếp cận dựa trên đề xuất của Philip , được cải tiến với tính năng kiểm tra loại bên trong của SFun28 cho Nullable
các loại:
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;
}
}
}
Tại sao là cái này? Tôi phải kiểm tra xem Type type
một số đã cho có phải là kiểu số hay không, và không nếu một tùy ý object o
là số.
Với C # 7, phương pháp này mang lại cho tôi hiệu suất tốt hơn so với việc bật trường hợp chuyển đổi TypeCode
và 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;
Các thử nghiệm sau:
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);
}
Bạn có thể sử dụng Type.IsPrimitive và sau đó sắp xếp Boolean
vàChar
loại loại, tương tự như sau:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
EDIT : Bạn có thể muốn loại trừ IntPtr
và UIntPtr
loại là tốt, nếu bạn không xem xét họ là số.
decimal
loại không phải là số?
Nhập phần mở rộng với hỗ trợ kiểu 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;
}
}
Sửa đổi của Skeet và giải pháp arviman của việc sử dụng Generics
, Reflection
và 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)
};
Theo dõi bởi:
public static bool IsNumeric<T>( this T myType )
{
var IsNumeric = false;
if( myType != null )
{
IsNumeric = m_numTypes.Contains( myType.GetType() );
}
return IsNumeric;
}
Sử dụng cho (T item)
:
if ( item.IsNumeric() ) {}
null
trả về false.
Chuyển đổi hơi chậm, vì mọi phương pháp trong tình huống xấu nhất sẽ được thực hiện tất cả các loại. Tôi nghĩ, sử dụng Dictionary thì tốt hơn, trong tình huống này, bạn sẽ có 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);
}
}
Hãy thử gói nuget TypeSupport cho C #. Nó có hỗ trợ phát hiện tất cả các kiểu số (trong số nhiều tính năng khác):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Thật không may, những loại này không có nhiều điểm chung ngoài việc chúng đều là các loại giá trị. Nhưng để tránh trường hợp chuyển đổi dài, bạn chỉ có thể xác định danh sách chỉ đọc với tất cả các loại này và sau đó chỉ cần kiểm tra xem loại đã cho có nằm trong danh sách hay không.
Chúng đều là các kiểu giá trị (ngoại trừ bool và có thể là enum). Vì vậy, bạn có thể chỉ cần sử dụng:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct
... Tôi không nghĩ đó là điều bạn muốn.
BIÊN TẬP: Chà, tôi đã sửa đổi mã bên dưới để hoạt động hiệu quả hơn và sau đó chạy các bài kiểm tra do @Hugo đăng để chống lại nó. Tốc độ ngang bằng với IF của @ Hugo bằng cách sử dụng mục cuối cùng trong chuỗi của anh ấy (Thập phân). Tuy nhiên, nếu sử dụng mục đầu tiên 'byte', anh ấy sẽ mất bánh, nhưng thứ tự rõ ràng là vấn đề quan trọng khi nói đến hiệu suất. Mặc dù sử dụng mã bên dưới dễ viết hơn và phù hợp hơn về chi phí, tuy nhiên, nó không phải là có thể bảo trì hoặc có thể kiểm chứng trong tương lai.
Có vẻ như việc chuyển từ Type.GetTypeCode () sang Convert.GetTypeCode () đã tăng tốc hiệu suất đáng kể, khoảng 25%, VS Enum.Parse () chậm hơn 10 lần.
Tôi biết bài đăng này đã cũ nhưng NẾU sử dụng phương pháp Enum TypeCode, đơn giản nhất (và có lẽ là rẻ nhất) sẽ là như thế này:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
Đưa ra định nghĩa enum sau cho 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
}
Tôi đã không kiểm tra nó kỹ lưỡng, nhưng đối với các loại số C # cơ bản, điều này dường như sẽ che đậy nó. Tuy nhiên, như @JonSkeet đã đề cập, enum này không được cập nhật cho các loại bổ sung được thêm vào .NET.
Giáo sư! Đọc sai câu hỏi! Cá nhân, sẽ cuộn với Skeet's .
HRM, âm thanh như bạn muốn DoSomething
trên Type
dữ liệu của bạn. Những gì bạn có thể làm là như sau
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;
}
}