¿Hay alguna forma de determinar si un tipo de .Net dado es un número o no? Por ejemplo: System.UInt32/UInt16/Doubleson todos números. Quiero evitar una larga caja de interruptores en el Type.FullName.
¿Hay alguna forma de determinar si un tipo de .Net dado es un número o no? Por ejemplo: System.UInt32/UInt16/Doubleson todos números. Quiero evitar una larga caja de interruptores en el Type.FullName.
Respuestas:
Prueba esto:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Los tipos primitivos son Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double y Single.
Llevando la solución de Guillaume un poco más allá:
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;
}
}
Uso:
int i = 32;
i.IsNumericType(); // True
string s = "Hello World";
s.IsNumericType(); // False
decimaltipo no es numérico?
decimal es numérico. El hecho de que no sea un primitivo no significa que no sea numérico. Su código debe tener en cuenta esto.
No use un interruptor, solo use un conjunto:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
EDITAR: Una ventaja de esto sobre el uso de un código de tipo es que cuando se introducen nuevos tipos numéricos en .NET (por ejemplo, BigInteger y Complex ) es fácil de ajustar, mientras que esos tipos no obtendrán un código de tipo.
switchsimplemente no funciona Type, así que no puedes. Puede encender, por TypeCodesupuesto, pero eso es un asunto diferente.
Ninguna de las soluciones tiene en cuenta Nullable.
Modifiqué un poco la solución de 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));
}
Sé que podría agregar los nullables a mi HashSet. Pero esta solución evita el peligro de olvidar agregar un Nullable específico a su lista.
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;
}
}
Nota sobre la optimización eliminada (ver comentarios de enzi)
Y si realmente quieres optimizarla (perdiendo legibilidad y algo de seguridad ...):
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;eliminar la rama introducida por &&.
Básicamente, la solución de Skeet, pero puede reutilizarla con tipos que aceptan valores NULL de la siguiente manera:
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);
}
}
Enfoque basado en la propuesta de Philip , mejorado con la verificación de tipo interna de SFun28 para Nullabletipos:
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;
}
}
}
¿Por qué esto? Tuve que verificar si un dado Type typees un tipo numérico y no si un arbitrario object oes numérico.
Con C # 7, este método me brinda un mejor rendimiento que encender la caja TypeCodey 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;
Las pruebas son las siguientes:
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);
}
Puede usar Type.IsPrimitive y luego ordenar los tipos Booleany Char, algo como esto:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
EDITAR : Es posible que desee excluir a los IntPtry UIntPtrtipos, así, si usted no se considera que sean numérico.
decimaltipo no es numérico?
Extensión de tipo con soporte de tipo nulo.
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;
}
}
De skeet modificados y solución de arviman utilización Generics, Reflectiony 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)
};
Seguido por:
public static bool IsNumeric<T>( this T myType )
{
var IsNumeric = false;
if( myType != null )
{
IsNumeric = m_numTypes.Contains( myType.GetType() );
}
return IsNumeric;
}
Uso para (T item):
if ( item.IsNumeric() ) {}
null devuelve falso.
El cambio es un poco lento, porque cada vez que los métodos en la peor situación pasarán por todos los tipos. Creo que usar Dictonary es más agradable, en esta situación tendrás 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);
}
}
Pruebe el paquete nuget TypeSupport para C #. Tiene soporte para detectar todos los tipos numéricos (entre muchas otras características):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Desafortunadamente, estos tipos no tienen mucho en común aparte de que son todos tipos de valor. Pero para evitar un caso de cambio largo, puede definir una lista de solo lectura con todos estos tipos y luego verificar si el tipo dado está dentro de la lista.
Todos son tipos de valor (excepto bool y tal vez enum). Entonces, simplemente podría usar:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct... No creo que eso sea lo que quieres.
EDITAR: Bueno, modifiqué el código a continuación para que sea más eficiente y luego ejecuté las pruebas publicadas por @Hugo en su contra. Las velocidades están aproximadamente a la par con el IF de @ Hugo usando el último elemento de su secuencia (decimal). Sin embargo, si usa el primer elemento 'byte', se lleva la palma, pero claramente el orden importa cuando se trata de rendimiento. Aunque usar el código a continuación es más fácil de escribir y más consistente en su costo, sin embargo, no se puede mantener ni se puede probar en el futuro.
Parece que cambiar de Type.GetTypeCode () a Convert.GetTypeCode () aceleró el rendimiento drásticamente, aproximadamente un 25%, VS Enum.Parse () que era como 10 veces más lento.
Sé que esta publicación es antigua, pero SI usa el método de enumeración TypeCode, lo más fácil (y probablemente el más barato) sería algo como esto:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
Dada la siguiente definición de enumeración para 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
}
No lo he probado a fondo, pero para los tipos numéricos básicos de C #, esto parece cubrirlo. Sin embargo, como mencionó @JonSkeet, esta enumeración no se actualiza para tipos adicionales agregados a .NET en el futuro.
¡Ups! ¡Leyó mal la pregunta! Personalmente, rodaría con Skeet's .
gestión de recursos humanos, como los sonidos que desea DoSomethingen Typesus datos. Lo que podrías hacer es lo siguiente
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;
}
}