web-dev-qa-db-fra.com

C # - comment déterminer si un type est un nombre

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 boîtier de commutation sur le Type.FullName.

89
Adi Barda

Essaye ça:

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 Single.

Prenant la solution de Guillaume 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;
  }
}

Usage:

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

string s = "Hello World";
s.IsNumericType(); // False
95
Philip Wallace

N'utilisez pas d'interrupteur - utilisez simplement un ensemble:

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

EDIT: Un avantage de cela par rapport à l'utilisation d'un code de type est que lorsque de nouveaux types numériques sont introduits dans .NET (par exemple BigInteger et Complex ), il est facile à ajuster - tandis que ces types n'obtiendra pas un code de type.

83
Jon Skeet

Aucune des solutions ne prend en compte Nullable.

J'ai un peu modifié la solution 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));
    }

Je sais que je pourrais simplement 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?),
        ...
    };
64
Jürgen Steinblock
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 supprimée (voir commentaires enzi) Et si vous voulez vraiment l'optimiser (perte de lisibilité et de 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;
}
35
Guillaume

Fondamentalement, la solution de Skeet, mais vous pouvez la réutiliser avec les types Nullable 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);
    }
}
14
arviman

Approche basée sur proposition de Philip , améliorée avec vérification du type interne de SFun28 pour les types Nullable:

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 ça? J'ai dû vérifier si un Type type est un type numérique, et pas si un arbitraire object o est numérique.

9
cimnine

Avec C # 7, cette méthode me donne de meilleures performances que de changer la casse 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 les 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);
    }
3
Hugo Freitas

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

[~ # ~] modifier [~ # ~] : vous souhaiterez peut-être exclure les types IntPtr et UIntPtr aussi, si vous ne les considérez pas comme étant numériques.

2
Konamiman

Réponse courte: Non.

Réponse plus longue: Non.

Le fait est que de nombreux types différents en C # peuvent contenir des données numériques. Sauf si vous savez à quoi vous attendre (Int, Double, etc.), vous devez utiliser la déclaration de cas "longue".

1
Craig

Cela peut également fonctionner. Cependant, vous voudrez peut-être le suivre avec un Type.Parse pour le diffuser comme vous le souhaitez par la suite.

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

EDIT: Eh bien, j'ai modifié le code ci-dessous pour être plus performant, puis j'ai exécuté les tests postés par @Hugo contre lui. Les vitesses sont à peu près équivalentes à celles de @ Hugo en utilisant le dernier élément de sa séquence (décimal). Si toutefois utiliser le premier élément "octet", il prend le gâteau, mais l'ordre est clairement important en matière de performances. Bien que l'utilisation du code ci-dessous soit plus facile à écrire et plus cohérente sur son coût, il n'est cependant pas maintenable ou à l'épreuve du temps.

Il semble que le passage de Type.GetTypeCode () à Convert.GetTypeCode () ait accéléré considérablement les performances, environ 25%, VS Enum.Parse () qui était 10 fois plus lent.


Je sais que ce message est ancien mais SI en utilisant la méthode d'énumération TypeCode, le plus simple (et probablement le moins cher) serait quelque chose comme ceci:

public static bool IsNumericType(this object o)
{   
  var t = (byte)Convert.GetTypeCode(o);
  return t > 4 && t < 16;
}

Étant donné la définition d'énumération suivante pour 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
}

Je ne l'ai pas testé à fond, mais pour les types numériques de base C #, cela semble le couvrir. Cependant, comme @JonSkeet l'a mentionné, cette énumération n'est pas mise à jour pour les types supplémentaires ajoutés à .NET ultérieurement.

0
Hector Bas

Ce sont tous des types de valeur (à l'exception de bool et peut-être enum). Vous pouvez donc simplement utiliser:

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

Modification de la solution de skeet et d'arviman en 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 renvoie false.

0
HouseCat

Le changement est un peu lent, car chaque fois que les méthodes dans la pire des situations passeront par tous les types. Je pense que l'utilisation de Dictonary est plus agréable, dans cette situation, vous aurez 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);
    }
}
0
Smagin Alexey

Malheureusement, ces types n'ont pas grand-chose en commun à part qu'ils sont tous des types de valeur. Mais pour éviter un long commutateur, vous pouvez simplement définir une liste en lecture seule avec tous ces types, puis vérifier simplement si le type donné se trouve dans la liste.

0
Darin Dimitrov