C#-タイプが数値かどうかを判断する方法


105

特定の.Netタイプが数値であるかどうかを判断する方法はありますか?たとえば、System.UInt32/UInt16/Doubleすべて数字です。の長いスイッチケースを避けたいですType.FullName


4
多くの、多くの、多くの重複。なぜこれはまだ閉じられていないのですか?
ノルドリン2009年

2
stackoverflow.com/questions/1130698の複製で、他のいくつかに非常に近い。
Henk Holterman、

回答:


110

これを試して:

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

プリミティブ型は、Boolean、Byte、SByte、Int16、UInt16、Int32、UInt32、Int64、UInt64、Char、Double、およびSingleです。

撮影ギヨームのソリューションをさらに少し:

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

使用法:

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

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

2
それで、decimal型は数値ではありませんか?
LukeH 2009年

2
@Xaero:私はそれdecimal 数値であることに疑いの余地ありません。それがプリミティブではないからといって、それが数値ではないという意味ではありません。あなたのコードはこれを説明する必要があります。
LukeH 2009年

2
これは、型コードを持たない.NET 4.0の新しい数値型に合わせて再設計する必要があります。
Jon Skeet、

7
現在のテクノロジーに基づいた回答について、どうすれば私に反対票を投じることができますか。たぶん.NET 62では、intは削除されます-あなたはintですべての回答に反対票を投じますか?
フィリップウォレス

1
@DiskJunkyすみません、友達。それはほぼ3年前のことで、彼らの内容が何であったか覚えていません。
kdbanman 2018年

93

スイッチを使用しないでください-セットを使用してください:

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

編集:型コードを使用することに対するこれの1つの利点は、新しい数値型が.NETに導入されると(BigIntegerComplexなど)、簡単に調整できることです。これらの型型コードを取得しません。


4
そして、どのようにHashSetを使用しますか?
RvdK 2009年

8
NumericTypes.Contains(whatever)?
mqp 2009年

3
bool isANumber = NumericTypes.Contains(classInstance.GetType());
Yuriy Faktorovich、2009年

コンパイラがスイッチステートメントをハッシュセットに暗黙的に変換すると考えていたでしょう。
Rolf Kristensen 2013

6
@RolfKristensen:まあswitch単純に動作しないTypeので、できません。TypeCodeもちろんスイッチを入れることもできますが、それは別の問題です。
Jon Skeet 2013

69

どのソリューションもNullableを考慮に入れていません。

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

Nullable自体を自分のHashSetに追加できることはわかっています。しかし、このソリューションは、特定のNullableをリストに追加し忘れる危険を回避します。

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

2
null許容型は本当に数値ですか?私の知る限り、ヌルは数ではありません。
IllidanS4はモニカを2014

2
それはあなたが達成したいことに依存します。私の場合、nullableも含める必要がありました。しかし、これが望ましい動作ではない状況も考えられます。
ユルゲン・Steinblock

良い!null入力可能な数値を数値として扱うことは、UI入力検証で非常に役立ちます。
guogangj

1
@ IllidanS4チェックはタイプではなく値で行われます。ほとんどの場合、null可能数値型は数値として処理する必要があります。もちろん、チェックが値に対して行われ、値がnullである場合、はい、数値とは見なされません。
nawfal

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

削除された最適化に関するメモ(enziコメントを参照) そして本当に最適化したい場合(読みやすさと安全性が失われます...):

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


13
私はこの答えが古いことを知っていますが、最近そのような切り替えに遭遇しました:提案された最適化を使用しないでください!私はそのようなスイッチから生成されたILコードを見て、コンパイラがすでに最適化を適用していることを指摘しました(IL 5ではタイプコードから差し引かれ、0から10までの値はtrueと見なされます)。したがって、より読みやすく、より安全で、同じくらい高速なスイッチを使用する必要があります。
enzi

1
実際に最適化したいが読みやすさを気にしない場合、最適なコードはreturn unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;によって導入されたブランチを削除することになり&&ます。
AnorZaken

14

基本的にスキートのソリューションですが、次のようにNullable型で再利用できます。

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

9

基づいたアプローチフィリップの提案を強化、SFun28の内側の型チェックのための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;
        }
    }
}

なんでこれ?私は与えられたものType typeが数値型であるかどうかをチェックしなければならず、任意のものobject oが数値型であるかどうかはチェックしませんでした。


4

C#7では、この方法を使用すると、スイッチをオンにしTypeCodeたり、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;

テストは次のとおりです。

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

Type.IsPrimitiveを使用して、次のようなBooleanCharタイプを整理できます。

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

編集:数値と見なさない場合は、IntPtrおよびUIntPtr型も除外することをお勧めします。


1
それで、decimal型は数値ではありませんか?
LukeH 2009年

おっと…まあ、結局ギヨームの解決策が最高だと思う。
コナミマン

3

null型をサポートする型拡張。

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

1

短い答え:いいえ。

より長い答え:いいえ。

実際、C#の多くの異なる型には数値データを含めることができます。何を期待するか(Int、Doubleなど)がわからない場合は、「長い」caseステートメントを使用する必要があります。


1

これもうまくいくかもしれません。ただし、Type.Parseでフォローアップして、後で必要な方法でキャストすることもできます。

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

1

修正スキートのとarvimanのソリューションを活用しGenericsReflection、と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)
};

に続く:

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

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

    return IsNumeric;
}

使用法(T item)

if ( item.IsNumeric() ) {}

null falseを返します。


1

最悪の状況のメソッドがすべてのタイプを通過するたびに、スイッチは少し遅くなります。私は、Dictonaryを使用する方がいいと思います。この状況では、次のようになります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);
    }
}

1

C#のTypeSupport nugetパッケージを試してください。(他の多くの機能の中で)すべての数値型の検出をサポートしています。

var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);

このパッケージを知りませんでした。OPが要求する種類の操作のために独自のコードを記述することを避けるために、多くの場合、救世主のようです。よろしくお願いします!
AFract

0

残念ながら、これらの型はすべて値型であることを除いて、共通点はあまりありません。しかし、長いスイッチケースを回避するために、これらすべてのタイプで読み取り専用リストを定義し、指定されたタイプがリスト内にあるかどうかを確認するだけで済みます。


0

これらはすべて値型です(ブール値およびおそらく列挙型を除く)。だからあなたは単に使うことができます:

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

1
これは、すべてのユーザー定義に対してtrueを返しますstruct...それはあなたが望むことではないと思います。
Dan Tao、

1
あなたは正しいです。組み込みの数値型も構造体です。だからプリミティブ比較で行くほうがいい。
MandoMando 2009年

0

編集:まあ、私はよりパフォーマンスが高くなるように以下のコードを変更し、それに対して@Hugoによって投稿されたテストを実行しました。速度は@HugoのIFとほぼ同じで、彼のシーケンスの最後の項目(10進数)を使用しています。ただし、最初のアイテム「バイト」を使用する場合、彼は簡単に理解できますが、パフォーマンスに関しては明らかに順序が重要です。以下のコードを使用すると、記述が簡単になり、コストの一貫性が高まりますが、保守や将来の保証はできません。

Type.GetTypeCode()からConvert.GetTypeCode()に切り替えると、パフォーマンスが大幅にスピードアップし、約25%、VS Enum.Parse()が10倍遅くなりました。


私はこの記事が古いです知っているが、IFこのようなものになるだろうされたTypeCode列挙法を使用して、最も簡単な(そしておそらく最も安いです):

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

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
}

私はそれを徹底的にテストしていませんが、基本的なC#数値型の場合、これでカバーできるようです。ただし、@ JonSkeetが述べたように、この列挙型は、将来.NETに追加される追加の型については更新されません。


-1

おっとっと!質問を誤解してください!個人的には、スキートのと一緒に転がるでしょう。


データをDoSomethingオンTypeにしたいようです。あなたができることは次のとおりです

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;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.