Gibt es eine Möglichkeit festzustellen, ob ein bestimmter .NET-Typ eine Zahl ist oder nicht? Zum Beispiel: System.UInt32/UInt16/Doublesind alle Zahlen. Ich möchte einen langen Schaltkasten am vermeiden Type.FullName.
Gibt es eine Möglichkeit festzustellen, ob ein bestimmter .NET-Typ eine Zahl ist oder nicht? Zum Beispiel: System.UInt32/UInt16/Doublesind alle Zahlen. Ich möchte einen langen Schaltkasten am vermeiden Type.FullName.
Antworten:
Versuche dies:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Die primitiven Typen sind Boolesch, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double und Single.
Unter Guillaume-Lösung ein wenig weiter:
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;
}
}
Verwendung:
int i = 32;
i.IsNumericType(); // True
string s = "Hello World";
s.IsNumericType(); // False
decimalTyp ist also nicht numerisch?
decimal das numerisch ist. Nur weil es kein Primitiv ist, heißt das nicht, dass es nicht numerisch ist. Ihr Code muss dies berücksichtigen.
Verwenden Sie keinen Schalter - verwenden Sie einfach ein Set:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
BEARBEITEN: Ein Vorteil gegenüber der Verwendung eines Typcodes besteht darin, dass bei der Einführung neuer numerischer Typen in .NET (z. B. BigInteger und Complex ) die Anpassung einfach ist - während diese Typen keinen Typcode erhalten.
switch, funktioniert einfach nicht Type, also kannst du nicht. Sie können TypeCodenatürlich einschalten , aber das ist eine andere Sache.
Keine der Lösungen berücksichtigt Nullable.
Ich habe Jon Skeets Lösung ein wenig modifiziert:
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));
}
Ich weiß, ich könnte einfach die Nullables selbst zu meinem HashSet hinzufügen. Diese Lösung vermeidet jedoch die Gefahr, dass Sie vergessen, Ihrer Liste eine bestimmte Nullable hinzuzufügen.
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;
}
}
Hinweis zur Optimierung entfernt (siehe enzi-Kommentare)
Und wenn Sie es wirklich optimieren möchten (Lesbarkeit und Sicherheit verlieren ...):
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;den von eingeführten Zweig entfernen &&.
Grundsätzlich Skeets Lösung, aber Sie können sie mit Nullable-Typen wie folgt wiederverwenden:
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);
}
}
Ansatz basierend auf Philipps Vorschlag , erweitert durch die innere Typprüfung von SFun28 für NullableTypen:
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;
}
}
}
Warum das? Ich musste überprüfen, ob ein gegebener Type typeein numerischer Typ ist und nicht, ob ein beliebiger numerischer Typ object oist.
Mit C # 7 bietet mir diese Methode eine bessere Leistung als das Einschalten des Gehäuses TypeCodeund 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;
Tests sind folgende:
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);
}
Sie könnten Type.IsPrimitive verwenden und dann die Booleanund Char-Typen aussortieren, ungefähr so:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
BEARBEITEN : Möglicherweise möchten Sie auch die Typen IntPtrund ausschließen UIntPtr, wenn Sie sie nicht als numerisch betrachten.
decimalTyp ist also nicht numerisch?
Typerweiterung mit Unterstützung vom Typ 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;
}
}
Geändert Skeet und arviman-Lösung verwendet Generics, Reflectionund 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)
};
Gefolgt von:
public static bool IsNumeric<T>( this T myType )
{
var IsNumeric = false;
if( myType != null )
{
IsNumeric = m_numTypes.Contains( myType.GetType() );
}
return IsNumeric;
}
Verwendung für (T item):
if ( item.IsNumeric() ) {}
null gibt false zurück.
Der Wechsel ist etwas langsam, da jedes Mal, wenn Methoden in der schlimmsten Situation alle Arten durchlaufen. Ich denke, Dictonary zu verwenden ist schöner, in dieser Situation haben Sie 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);
}
}
Probieren Sie das TypeSupport- Nuget-Paket für C # aus. Es unterstützt die Erkennung aller numerischen Typen (neben vielen anderen Funktionen):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Leider haben diese Typen nicht viel gemeinsam, außer dass sie alle Werttypen sind. Um einen langen Wechsel zu vermeiden, können Sie einfach eine schreibgeschützte Liste mit all diesen Typen definieren und dann überprüfen, ob der angegebene Typ in der Liste enthalten ist.
Sie sind alle Werttypen (außer bool und möglicherweise enum). Sie können also einfach Folgendes verwenden:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct" zurückgeben. Ich glaube nicht, dass Sie dies wünschen.
EDIT: Nun, ich habe den folgenden Code geändert, um eine bessere Leistung zu erzielen, und dann die von @Hugo veröffentlichten Tests dagegen ausgeführt. Die Geschwindigkeiten sind ungefähr gleich hoch wie bei @ Hugos IF, wenn das letzte Element in seiner Sequenz (Dezimal) verwendet wird. Wenn Sie jedoch das erste Element "Byte" verwenden, nimmt er den Kuchen, aber die Reihenfolge ist eindeutig wichtig, wenn es um die Leistung geht. Die Verwendung des folgenden Codes ist zwar einfacher zu schreiben und hinsichtlich der Kosten konsistenter, jedoch nicht wartbar oder zukunftssicher.
Der Wechsel von Type.GetTypeCode () zu Convert.GetTypeCode () hat die Leistung drastisch beschleunigt, etwa 25% gegenüber VS Enum.Parse (), was etwa zehnmal langsamer war.
Ich weiß , dass dieser Beitrag ist alt, aber IF die Typecode - Enumeration - Methode, einfachste (und wahrscheinlich das billigste) würde wie folgt sein:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
Angesichts der folgenden Aufzählungsdefinition für 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
}
Ich habe es nicht gründlich getestet, aber für grundlegende C # -Numeriktypen scheint dies zu reichen. Wie bereits von @JonSkeet erwähnt, wird diese Aufzählung jedoch nicht für zusätzliche Typen aktualisiert, die später zu .NET hinzugefügt werden.
Hoppla! Die Frage falsch verstanden! Persönlich würde mit Skeet rollen .
HRM, klingt wie Sie wollen DoSomethingauf TypeIhre Daten. Was Sie tun könnten, ist das Folgende
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;
}
}