Czy istnieje sposób określenia, czy dany typ .Net jest liczbą? Na przykład: System.UInt32/UInt16/Doubleto wszystkie liczby. Chcę uniknąć długiego przełączania na Type.FullName.
Czy istnieje sposób określenia, czy dany typ .Net jest liczbą? Na przykład: System.UInt32/UInt16/Doubleto wszystkie liczby. Chcę uniknąć długiego przełączania na Type.FullName.
Odpowiedzi:
Spróbuj tego:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Typy pierwotne to Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double i Single.
Biorąc roztwór Guillaume trochę dalej:
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;
}
}
Stosowanie:
int i = 32;
i.IsNumericType(); // True
string s = "Hello World";
s.IsNumericType(); // False
decimaltyp nie jest numeryczny?
decimal jest to numeryczne. To, że nie jest prymitywem, nie oznacza, że nie jest numeryczne. Twój kod musi to uwzględniać.
Nie używaj przełącznika - po prostu użyj zestawu:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
EDYCJA: Jedną z zalet tego w porównaniu z używaniem kodu typu jest to, że kiedy nowe typy liczbowe są wprowadzane do .NET (np. BigInteger i Complex ), jest to łatwe do dostosowania - podczas gdy te typy nie otrzymają kodu typu.
switchprostu nie działa Type, więc nie możesz. Możesz TypeCodeoczywiście włączyć , ale to inna sprawa.
Żadne z rozwiązań nie bierze pod uwagę wartości Nullable.
Zmodyfikowałem nieco rozwiązanie Jona Skeeta:
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));
}
Wiem, że mógłbym po prostu dodać same wartości null do mojego HashSet. Ale to rozwiązanie pozwala uniknąć niebezpieczeństwa zapomnienia o dodaniu określonego Nullable do listy.
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;
}
}
Uwaga o usuniętej optymalizacji (zobacz komentarze enzi)
A jeśli naprawdę chcesz ją zoptymalizować (utrata czytelności i trochę bezpieczeństwa ...):
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;usunięcie gałęzi wprowadzonej przez &&.
Zasadniczo rozwiązanie Skeeta, ale możesz go ponownie użyć z typami Nullable w następujący sposób:
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);
}
}
Podejście oparte na propozycji Philipa , wzbogacone o kontrolę typu wewnętrznego SFun28 dla Nullabletypów:
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;
}
}
}
Dlaczego to? Musiałem sprawdzić, czy dany Type typejest typem liczbowym, a nie, czy dowolny object ojest liczbowy.
Z C # 7 ta metoda daje mi lepszą wydajność niż włączanie obudowy TypeCodei 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;
Testy są następujące:
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);
}
Możesz użyć Type.IsPrimitive, a następnie posortować typy Booleani Char, coś takiego:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
EDIT : Możesz wyłączyć IntPtri UIntPtrrodzajów, jak również, jeśli nie uważają ich za numeryczny.
decimaltyp nie jest numeryczny?
Rozszerzenie typu z obsługą typu 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;
}
}
Zmodyfikowane Skeet tych i rozwiązanie arviman za wykorzystanie Generics, Reflectionoraz 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)
};
Śledzony przez:
public static bool IsNumeric<T>( this T myType )
{
var IsNumeric = false;
if( myType != null )
{
IsNumeric = m_numTypes.Contains( myType.GetType() );
}
return IsNumeric;
}
Użycie do (T item):
if ( item.IsNumeric() ) {}
null zwraca false.
Zmiana jest trochę powolna, ponieważ za każdym razem metody w najgorszej sytuacji będą przechodzić przez wszystkie typy. Myślę, że korzystanie z Dictonary jest przyjemniejsze, w takiej sytuacji będziesz miał 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);
}
}
Wypróbuj pakiet NuGet TypeSupport dla języka C #. Obsługuje wykrywanie wszystkich typów liczbowych (wśród wielu innych funkcji):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Niestety, te typy nie mają wiele wspólnego, poza wszystkimi typami wartości. Ale aby uniknąć długich przypadków przełączania, możesz po prostu zdefiniować listę tylko do odczytu ze wszystkimi tymi typami, a następnie po prostu sprawdzić, czy dany typ znajduje się na liście.
Wszystkie są typami wartości (z wyjątkiem bool i może enum). Możesz więc po prostu użyć:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct... Nie sądzę, że tego chcesz.
EDYCJA: Cóż, zmodyfikowałem poniższy kod, aby był bardziej wydajny, a następnie przeprowadziłem testy opublikowane przez @Hugo. Prędkości są mniej więcej na równi z JEŻELI @ Hugo przy użyciu ostatniego elementu w jego sekwencji (dziesiętne). Jeśli jednak użyje pierwszego elementu „bajt”, to bierze ciasto, ale wyraźnie porządek ma znaczenie, jeśli chodzi o wydajność. Chociaż użycie poniższego kodu jest łatwiejsze do napisania i bardziej spójne pod względem kosztów, nie jest jednak możliwe do utrzymania ani zabezpieczenia w przyszłości.
Wygląda na to, że przejście z Type.GetTypeCode () na Convert.GetTypeCode () drastycznie przyspieszyło wydajność, około 25%, VS Enum.Parse (), które było około 10 razy wolniejsze.
Wiem, że ten post jest stary, ale JEŚLI użyjesz metody wyliczenia TypeCode, najłatwiej (i prawdopodobnie najtańszy) będzie coś takiego:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
Biorąc pod uwagę następującą definicję wyliczenia dla 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
}
Nie przetestowałem go dokładnie, ale w przypadku podstawowych typów numerycznych C # wydaje się, że to obejmuje. Jednak, jak wspomniał @JonSkeet, to wyliczenie nie jest aktualizowane dla dodatkowych typów dodanych do .NET w przyszłości.
ups! Źle przeczytaj pytanie! Osobiście kroczyłby z Skeetem .
HRM, dźwięki tak, jak chcesz, aby DoSomethingna TypeTwoich danych. Oto co możesz zrobić
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;
}
}