2009-11-17 4 views
73

Existe-t-il un moyen de déterminer si un Type .Net donné est un nombre? Par exemple: System.UInt32/UInt16/Double sont tous des nombres. Je veux éviter un long interrupteur sur le Type.FullName.C# - comment déterminer si un Type est un nombre

+4

Dupe de beaucoup, beaucoup, beaucoup. Pourquoi cela n'a-t-il pas encore été fermé? – Noldorin

+2

Dupliquer de http://stackoverflow.com/questions/1130698/ et très proche de certains autres. –

+0

duplication possible de [En utilisant .Net, comment puis-je déterminer si un type est un ValueType numérique?] (Http: // stackoverflow.com/questions/124411/using-net-comment-peut-je-déterminer-si-un-type-est-un-type-valeur-numérique) – nawfal

Répondre

77

Essayez ceci:

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

Les types primitifs sont Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double et unique.

Prendre Guillaume's solution un peu plus loin:

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

Utilisation:

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

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

Donc le type 'decimal' n'est pas numérique? – LukeH

+0

Les types primitifs sont les suivants: Booléen, Octet, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double et Single. –

+0

@Luke - bonne question. C'est une structure - vous devez donc définir numérique. –

2

Vous pouvez utiliser Type.IsPrimitive puis trier les types Boolean et Char, quelque chose comme ceci:

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

EDIT: Vous pouvez exclure les IntPtr et UIntPtr types aussi bien, si vous n » Je considère qu'ils sont numériques.

+0

Je sais, j'ai mis à jour la réponse pendant que vous commentiez. – Konamiman

+1

Donc le type 'decimal' n'est pas numérique? – LukeH

+0

Ooops ... eh bien, il semble que la solution de Guillaume soit la meilleure après tout. – Konamiman

0

Malheureusement, ces types n'ont pas beaucoup en commun, sauf qu'ils sont tous les types de valeurs. Mais pour éviter un long switch-case, vous pouvez simplement définir une liste en lecture seule avec tous ces types, puis vérifier simplement si le type donné est dans la liste.

69

Ne pas utiliser un interrupteur - il suffit d'utiliser un ensemble:

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

EDIT: Un avantage de ce rapport à l'utilisation d'un code de type est que lorsque les nouveaux types numériques sont introduits dans .NET (par exemple BigInteger et Complex) il est facile à ajuster - alors que les types ne vont pas obtenir un code de type.

+3

et comment vous utiliseriez le HashSet? – RvdK

+7

NumericTypes.Contains (whatever)? – mquander

+2

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

1

Réponse courte: Non

Longer Réponse: Non.

Le fait est que de nombreux types différents en C# peuvent contenir des données numériques. Si vous ne savez pas à quoi vous attendre (Int, Double, etc.), vous devez utiliser l'instruction "long".

0

oups! Mal lire la question! Personnellement, roulerait avec Skeet's.


HRM, sonne comme vous voulez DoSomething sur Type de vos données. Ce que vous pouvez faire est le suivant

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

Ils sont tous les types de valeur (sauf pour bool et peut-être enum).Donc, vous pouvez simplement utiliser:

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

Cela retournera true pour toute 'struct' définie par l'utilisateur ... Je ne pense pas que ce soit ce que vous voulez. –

+1

Vous avez raison. Les types numériques intégrés sont également des structures. Il vaut donc mieux aller avec la comparaison Primitive alors. – 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; 
    } 
} 

Remarque sur l'optimisation retirée (voir les commentaires Enzi) Et si vous voulez vraiment optimiser (perdre la lisibilité et une certaine sécurité ...):

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

Je sais que cette réponse est ancienne, mais je suis récemment tombé sur un tel changement: n'utilisez pas l'optimisation suggérée! J'ai regardé le code IL généré à partir d'un tel commutateur, et noté que le compilateur applique déjà l'optimisation (dans IL 5 est soustrait du code de type et ensuite les valeurs de 0 à 10 sont considérées comme vraies). D'où le commutateur devrait être utilisé pour qu'il soit plus lisible et plus sûr et tout aussi rapide. – enzi

+0

Si vous voulez vraiment l'optimiser et que vous ne vous souciez pas de la lisibilité, le code optimal serait 'return non coché ((uint) Type.GetTypeCode (type) - 5u) <= 10u;' supprimant ainsi la branche introduite par '&&' . – AnorZaken

49

Aucune des solutions prend Nul lable en compte.

J'ai modifié la solution de Jon Skeet un peu:

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

Je sais que je peux ajouter les nullables lui-même à mon HashSet. Mais cette solution évite le danger d'oublier d'ajouter un Nullable spécifique à votre liste.

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

Un type nullable est-il vraiment numérique? Null n'est pas un nombre, à ma connaissance. – IllidanS4

+0

Cela dépend de ce que vous voulez réaliser. Dans mon cas, j'avais aussi besoin d'inclure des valeurs nulles. Mais je pourrais aussi penser à des situations où ce n'est pas un comportement désiré. –

8

approche basée sur Philip's proposal, renforcée avec SFun28's inner type check pour Nullable types:

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

Pourquoi? J'ai dû vérifier si un Type type donné est un type numérique, et non si un object o arbitraire est numérique.

8

Fondamentalement, la solution de Skeet, mais vous pouvez le réutiliser avec les types nullables comme suit:

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

Cela peut fonctionner aussi bien. Cependant, vous pouvez le suivre avec un Type.Parse pour le lancer comme vous le souhaitez par la suite.

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

modifiés au pigeon d'argile et de la solution de arviman utilisant Generics, Reflection et 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) 
}; 

Suivi par:

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

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

    return IsNumeric; 
} 

Utilisation pour (T item):

if (item.IsNumeric()) {} 

null retourne false.

0

Avec C# 7 cette méthode me donne de meilleures performances que le cas de l'interrupteur sur TypeCode et 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; 

Les tests sont suivants:

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