Existe uma maneira de determinar se um determinado tipo .Net é ou não um número? Por exemplo: System.UInt32/UInt16/Double
são todos números. Quero evitar um longo caso de interruptor no Type.FullName
.
Existe uma maneira de determinar se um determinado tipo .Net é ou não um número? Por exemplo: System.UInt32/UInt16/Double
são todos números. Quero evitar um longo caso de interruptor no Type.FullName
.
Respostas:
Experimente isto:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Os tipos primitivos são Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double e Single.
Levando a solução de Guillaume um pouco mais longe:
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
decimal
tipo não é numérico?
decimal
é numérico. Só porque não é um primitivo, não significa que não seja numérico. Seu código precisa levar em conta isso.
Não use uma chave - apenas use um conjunto:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
EDIT: Uma vantagem disso sobre o uso de um código de tipo é que quando novos tipos numéricos são introduzidos no .NET (por exemplo, BigInteger e Complex ) é fácil de ajustar - enquanto esses tipos não recebem um código de tipo.
switch
simplesmente não funciona Type
, então você não pode. Você pode ligar TypeCode
, é claro, mas isso é diferente.
Nenhuma das soluções leva Nullable em consideração.
Modifiquei um pouco a solução 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));
}
Eu sei que poderia apenas adicionar os próprios nulos ao meu HashSet. Mas esta solução evita o perigo de esquecer de adicionar um Nullable específico à sua 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 a otimização removida (ver comentários da enzi)
E se você realmente deseja otimizá-la (perdendo legibilidade e alguma segurança ...):
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;
remover o desvio introduzido por &&
.
Basicamente a solução de Skeet, mas você pode reutilizá-la com tipos anuláveis da seguinte forma:
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);
}
}
Abordagem baseada na proposta de Philip , aprimorada com a verificação de tipo interna do SFun28 para Nullable
tipos:
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;
}
}
}
Porque isso? Tive que verificar se um dado Type type
é um tipo numérico, e não se um arbitrário object o
é numérico.
Com o C # 7, esse método me dá melhor desempenho do que ligar a caixa TypeCode
e 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;
Os testes são os seguintes:
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);
}
Você poderia usar Type.IsPrimitive e, em seguida, classificar os tipos Boolean
e Char
, algo assim:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
EDITAR : Você pode excluir os tipos IntPtr
e UIntPtr
também, se não os considerar numéricos.
decimal
tipo não é numérico?
Extensão de tipo com suporte 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;
}
}
Skeet modificados e solução de arviman utilizando Generics
, Reflection
e 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
retorna falso.
O switch é um pouco lento, porque toda vez que os métodos na pior situação passarão por todos os tipos. Eu acho que usar o Dictonário é mais legal, nessa situação você terá 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);
}
}
Experimente o pacote nuget TypeSupport para C #. Possui suporte para detecção de todos os tipos numéricos (entre muitos outros recursos):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Infelizmente, esses tipos não têm muito em comum além de serem todos tipos de valor. Mas para evitar um longo caso de alternância, você poderia apenas definir uma lista somente leitura com todos esses tipos e, em seguida, apenas verificar se o tipo fornecido está dentro da lista.
Eles são todos tipos de valor (exceto para bool e talvez enum). Então, você pode simplesmente usar:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct
... Não acho que é isso que você deseja.
EDIT: Bem, eu modifiquei o código abaixo para ter mais desempenho e, em seguida, executei os testes postados por @Hugo contra ele. As velocidades estão quase no mesmo nível do IF de @Hugo usando o último item de sua sequência (decimal). No entanto, se usar o primeiro item 'byte', ele leva o bolo, mas claramente a ordem é importante quando se trata de desempenho. Embora usar o código abaixo seja mais fácil de escrever e mais consistente em seu custo, ele não pode ser mantido ou preparado para o futuro.
Parece que mudar de Type.GetTypeCode () para Convert.GetTypeCode () acelerou o desempenho drasticamente, cerca de 25%, VS Enum.Parse () que era cerca de 10 vezes mais lento.
Eu sei que esta postagem é antiga, mas SE você estiver usando o método enum TypeCode, o mais fácil (e provavelmente o mais barato) seria algo assim:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
Dada a seguinte definição de enum 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
}
Eu não testei completamente, mas para tipos numéricos C # básicos, isso parece cobrir tudo. No entanto, como @JonSkeet mencionou, este enum não é atualizado para tipos adicionais adicionados ao .NET no futuro.
oops! Interpretou mal a pergunta! Pessoalmente, iria rolar com Skeet's .
HRM, parece que você quer DoSomething
na Type
de seus dados. O que você poderia fazer é o seguinte
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;
}
}