2009-11-17 12 views
73

C'è un modo per determinare se un determinato .Net Type è un numero? Ad esempio: System.UInt32/UInt16/Double sono tutti i numeri. Voglio evitare una lunga commutazione sulla Type.FullName.C# - come determinare se un tipo è un numero

+4

Dupe di molti, molti, molti. Perché non è stato ancora chiuso? – Noldorin

+2

Duplicato di http://stackoverflow.com/questions/1130698/ e molto vicino ad altri. –

+0

possibile duplicato di [Utilizzo di .Net, come posso determinare se un tipo è un valore numerico?] (Http: // stackoverflow.it/questions/124411/using-net-how-can-i-determin-if-a-type-is-a-numeric-valuetype) – nawfal

risposta

77

Prova questo:

Type type = object.GetType(); 
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char)); 

I tipi primitivi sono booleano, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, doppio e singolo .

Prendendo Guillaume's solution un po 'più avanti:

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; 
    } 
} 

utilizzo:

int i = 32; 
i.IsNumericType(); // True 

string s = "Hello World"; 
s.IsNumericType(); // False 
+2

Quindi il tipo 'decimale' non è numerico? – LukeH

+0

I tipi primitivi sono Booleano, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double e Single. –

+0

@Luke - buona domanda. È una struttura - quindi è necessario definire numerici. –

2

Si potrebbe utilizzare Type.IsPrimitive e quindi risolvere i tipi Boolean e Char, qualcosa di simile:

bool IsNumeric(Type type) 
{ 
    return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool); 
} 

EDIT: Si consiglia di escludere le e UIntPtr tipi IntPtr pure, se don' li considero numerici.

+0

Lo so, ho aggiornato la risposta mentre stavi commentando. – Konamiman

+1

Quindi il tipo 'decimale' non è numerico? – LukeH

+0

Ooops ... beh, sembra che la soluzione di Guillaume sia la migliore dopo tutto. – Konamiman

0

Sfortunatamente questi tipi non hanno molto in comune tranne che sono tutti tipi di valore. Ma per evitare una lunga commutazione, basta semplicemente definire un elenco di sola lettura con tutti questi tipi e quindi controllare se il tipo specificato è all'interno dell'elenco.

69

Non utilizzare un interruttore - basta usare un set:

HashSet<Type> NumericTypes = new HashSet<Type> 
{ 
    typeof(decimal), typeof(byte), typeof(sbyte), 
    typeof(short), typeof(ushort), ... 
}; 

EDIT: Un vantaggio di questo corso utilizzando un codice tipo è che quando i nuovi tipi numerici sono introdotti in .NET (ad esempio BigInteger e Complex) è facile da regolare, mentre i tipi non riceveranno un codice di tipo.

+3

e come si usa HashSet? – RvdK

+7

NumericTypes.Contains (qualunque)? – mquander

+2

bool isANUM = NumericTypes.Contains (classInstance.GetType()); –

1

Risposta breve: n.

Risposta più lunga: No.

Il fatto è che molti tipi diversi in C# possono contenere dati numerici. A meno che tu non sappia cosa aspettarti (Int, Double, ecc.) Devi usare la frase "lunga".

0

oops! Ho letto male la domanda! Personalmente, rotolerei con Skeet's.


gestione delle risorse umane, suona come si desidera DoSomething su Type dei tuoi dati. Quello che potresti fare è il seguente

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; 
    } 
} 
1

Sono tutti tipi di valori (ad eccezione di bool e forse enum).Così si potrebbe usare semplicemente:

bool IsNumberic(object o) 
{ 
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum)) 
} 
+1

Questo restituirà true per qualsiasi 'struct' definito dall'utente ... Non penso che sia quello che vuoi. –

+1

Sei corretto. Anche i tipi numerici incorporati sono strutture. Quindi meglio andare con il confronto Primitivo quindi. – MandoMando

28
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 sull'ottimizzazione rimosso (vedi commenti Enzi) E se si vuole veramente per ottimizzarlo (perdendo leggibilità e un po 'di sicurezza ...):

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; 
} 

+12

So che questa risposta è vecchia, ma di recente mi sono imbattuto in un simile switch: non utilizzare l'ottimizzazione suggerita! Ho esaminato il codice IL generato da tale switch e ho notato che il compilatore applica già l'ottimizzazione (in IL 5 viene sottratto dal codice del tipo e quindi i valori da 0 a 10 sono considerati veri). Quindi l'interruttore dovrebbe essere utilizzato per renderlo più leggibile e sicuro e altrettanto veloce. – enzi

+0

Se si desidera ottimizzarlo e non interessa la leggibilità, il codice ottimale sarebbe 'return deselezionato ((uint) Type.GetTypeCode (type) - 5u) <= 10u;' rimuovendo quindi il ramo introdotto da '&&' . – AnorZaken

49

Nessuna delle soluzioni prende Nul in considerazione.

Ho modificato la soluzione di Jon Skeet un po ':

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)); 
    } 

So che potrei aggiungere appena i nullables si alla mia HashSet. Ma questa soluzione evita il pericolo di dimenticare di aggiungere uno specifico Nullable alla tua lista.

private static HashSet<Type> NumericTypes = new HashSet<Type> 
    { 
     typeof(int), 
     typeof(int?), 
     ... 
    }; 
+1

Un tipo nullable è davvero numerico? Null non è un numero, per quanto ne so. – IllidanS4

+0

Dipende da ciò che si vuole ottenere. Nel mio caso dovevo includere anche il nullable. Ma potrei anche pensare a situazioni in cui questo non è un comportamento desiderato. –

8

approccio sulla base di Philip's proposal, migliorato con SFun28's inner type check per Nullable tipi:

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; 
     } 
    } 
} 

Perchè questo? Ho dovuto verificare se un dato Type type è un tipo numerico, e non se un arbitrario object o è numerico.

8

In sostanza la soluzione di Skeet, ma è possibile riutilizzarlo con i tipi Nullable come segue:

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); 
    } 
} 
1

Questo può funzionare così. Tuttavia, si consiglia di seguirlo con un Type.Parse per lanciarlo come lo si desidera in seguito.

public bool IsNumeric(object value) 
{ 
    float testValue; 
    return float.TryParse(value.ToString(), out testValue); 
} 
0

skeet modificati di e la soluzione di arviman utilizzando 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) 
}; 

seguito da:

public static bool IsNumeric<T>(this T myType) 
{ 
    var IsNumeric = false; 

    if(myType != null) 
    { 
     IsNumeric = m_numTypes.Contains(myType.GetType()); 
    } 

    return IsNumeric; 
} 

di utilizzo per (T item):

if (item.IsNumeric()) {} 

null restituisce false.

0

Con C# 7 di questo metodo dà a me prestazioni migliori rispetto caso accendere 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; 

I test sono le seguenti:

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); 
    } 
Problemi correlati