オブジェクトがC#の数値であるかどうかの確認


89

オブジェクトが数値であるかどうかを確認したいので、.ToString()数字とを含む文字列になります+-.

.netのような単純な型チェックで可能if (p is Number)ですか(例:)?

または、文字列に変換してから、解析してdoubleにしてみますか?

更新:オブジェクトがint、uint、float、doubleなどであることを明確にするために、それは文字列ではありません。次のように、オブジェクトをxmlにシリアル化する関数を作成しようとしています。

<string>content</string>

または

<numeric>123.3</numeric>

または例外を発生させます。


5
あなたはあなた自身のXmlSerializerを書こうとしているように聞こえます-.NET- msdn.microsoft.com/en-us/library / ...
RichardOD 2009

2
XSDを使用してXML形式を定義し、付属のXSDツールを使用してデータをシリアル化できるオブジェクトを作成することで、この問題全体を回避できる場合があります-msdn.microsoft.com/en-us/library/x6c1kb0s %28VS.71%29.aspx
デクスター

@RichardOD:XMLシリアル化を使用してオブジェクトをシリアル化できますか?私はそれがフラッシュ関数を呼び出す必要がありadobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/...を
ピョートルCzapla

回答:


180

基本的な数値型のそれぞれについて型チェックを行うだけです。

これは仕事をする必要がある拡張メソッドです:

public static bool IsNumber(this object value)
{
    return value is sbyte
            || value is byte
            || value is short
            || value is ushort
            || value is int
            || value is uint
            || value is long
            || value is ulong
            || value is float
            || value is double
            || value is decimal;
}

これはすべての数値タイプをカバーする必要があります。

更新

逆シリアル化中に文字列の数値を実際に解析する必要があるようです。この場合、おそらくを使用するのが最善double.TryParseです。

string value = "123.3";
double num;
if (!double.TryParse(value, out num))
    throw new InvalidOperationException("Value is not a number.");

もちろん、これは非常に大きな整数/長い小数を扱えないだろうが、その場合はあなただけへの追加の呼び出しを追加する必要がありますlong.TryParse/ decimal.TryParse他/何でも。


私のオブジェクトは、int、short、uint、float、doubleなど、数値であるものです
Piotr Czapla 2009

@Piotr:そうだね。誤解しているようです。私の更新された答えを見てください。
ノルドリン2009

1
@Noldorin:実際には、以前のバージョンのコードも機能します。nullチェックを追加して、value.ToString()を使用するだけです。その後、すべての数値タイプをチェックする必要はありません。
FredrikMörk、

1
。それは、それが正しい解決策は、その冗長であること:(悲しいです@Noldorin
ピョートルCzapla

1
@Joe:実際には、ToStringも現在のカルチャを使用するため、違いはありません。
ノルドリン2009

36

スコット・ハンセルマンのブログから引用:

public static bool IsNumeric(object expression)
{
    if (expression == null)
    return false;

    double number;
    return Double.TryParse( Convert.ToString( expression
                                            , CultureInfo.InvariantCulture)
                          , System.Globalization.NumberStyles.Any
                          , NumberFormatInfo.InvariantInfo
                          , out number);
}

6
このアプローチの問題は、数値のように見える文字列を渡した場合にフォーマットされることです。ほとんどの人にとっては大丈夫かもしれませんが、それは私にとってショーストッパーでした。
Rob Sedgwick、

1
これに関するもう1つの潜在的な問題は、最小値と最大値をdoubleで解析できないことです。double.Parse(double.MaxValue.ToString())を引き起こしますOverflowException.ToString("R")この場合は、ラウンドトリップ修飾子を提供することでこれを修正できますConvert.ToString(...)が、タイプがわからないため、そのオーバーロードは利用できません。これはちょっとした問題だと知っていますが、自分の.IsNumeric()拡張機能のテストを書いているときに偶然見つけました。私の「解決策」は、何かを解析する前に型チェックswtichを追加することでした。コードについては、この質問に対する私の回答を参照してください。
Ben

21

IsPrimitiveプロパティを利用して、便利な拡張メソッドを作成します。

public static bool IsNumber(this object obj)
{
    if (Equals(obj, null))
    {
        return false;
    }

    Type objType = obj.GetType();
    objType = Nullable.GetUnderlyingType(objType) ?? objType;

    if (objType.IsPrimitive)
    {
        return objType != typeof(bool) && 
            objType != typeof(char) && 
            objType != typeof(IntPtr) && 
            objType != typeof(UIntPtr);
    }

    return objType == typeof(decimal);
}

編集:コメントに従って修正されました。.GetType()は値の型をボックス化するため、ジェネリックは削除されました。null許容値の修正も含まれています。


1
ジェネリックスの部分では、ここで余分なことは何もありませんね。オブジェクトで使用可能なGetType()にのみアクセスします...
Peter Lillevold 2009

値型で呼び出された場合、1つのボックス操作を保存します。再利用性を考えてください。
ケナンEK

1
obj.GetTypeの代わりにtypeof(T)を使用しないのはなぜですか。そうすれば、誰かがnull参照型を渡した場合にNullReferenceExceptionが発生しません。値型のみを受け入れるようにTに一般的な制約を課すこともできます。もちろん、それを行うと、コンパイル時に多くの情報を取得し始めます。
Trillian、

objectおよびstringプリミティブ型ではありません。
私たちは全員モニカです

@jnylen:この答えはかなり前のことです。当時、リフレクタ化されたフレームワークのソースから何かを掘り出したと思いますが、今日は誰が言うことができますか?正解です。
ケナンEK 2012

10

上記のいくつかの素晴らしい答えがあります。これがオールインワンのソリューションです。さまざまな状況での3つのオーバーロード。

// Extension method, call for any object, eg "if (x.IsNumeric())..."
public static bool IsNumeric(this object x) { return (x==null ? false : IsNumeric(x.GetType())); }

// Method where you know the type of the object
public static bool IsNumeric(Type type) { return IsNumeric(type, Type.GetTypeCode(type)); }

// Method where you know the type and the type code of the object
public static bool IsNumeric(Type type, TypeCode typeCode) { return (typeCode == TypeCode.Decimal || (type.IsPrimitive && typeCode != TypeCode.Object && typeCode != TypeCode.Boolean && typeCode != TypeCode.Char)); }

nullチェックを追加することを検討してください
wiero

本当にnullチェックは必要ありません-拡張メソッドとして、null値で呼び出すことはできませんでした。もちろん、誰かが通常の関数として呼び出すことはできますが、これは拡張メソッドの予想される使用法ではありません。
ミックブルーノ

5
null値で呼び出すことができると思います。オブジェクトobj = null; obj.IsNumeric();
wiero 14

Weiroに感謝します。修正しました。null値で拡張メソッドを呼び出すことが可能であることに気づかなかったが、もちろん可能である!
ミックブルーノ

最初のオーバーロードの最後に括弧がないと思います: "return(x == null?false:IsNumeric(x.GetType()));"
glenn garson '28

8

独自にローリングするのではなく、組み込み型が数値であるかどうかを確認する最も信頼できる方法は、を参照Microsoft.VisualBasicして呼び出すことInformation.IsNumeric(object value)です。実装は、char[]HEXやOCT文字列などの多くの微妙なケースを処理します。


これは一番上にあるはずです!
nawfal

4

そこでは3つの異なる概念があります。

  • 数値(つまり、(通常はボックス化された)数値自体)かどうかを確認するに、次のようにタイプを確認しますis-たとえばif(obj is int) {...}
  • 文字列を数値として解析できるかどうかを確認します。使用するTryParse()
  • しかし、オブジェクトが数値または文字列ではないが、数値のToString()ように見えるものを与える可能性があると思われる場合は、呼び出し ToString()て文字列として扱います

最初の2つのケースの両方で、サポートする数値タイプを個別に処理する必要があるでしょう(double/ decimal/int)があります(たとえば、それぞれに異なる範囲と精度があります)。

また、簡単な大まかなチェックのために正規表現を見ることができます。


4

入力が文字列であると仮定します...

2つの方法があります。

Double.TryParse()を使用します

double temp;
bool isNumber = Double.TryParse(input, out temp);

正規表現を使用する

 bool isNumber = Regex.IsMatch(input,@"-?\d+(\.\d+)?");

3

次のようなコードを使用できます。

if (n is IConvertible)
  return ((IConvertible) n).ToDouble(CultureInfo.CurrentCulture);
else
  // Cannot be converted.

あなたのオブジェクトがある場合はInt32SingleDoubleなどは、変換を実行します。また、文字列は実装されますIConvertibleが、文字列がdoubleに変換できない場合は、a FormatExceptionがスローされます。


実際には文字列が解析されますが、正しい形式でない場合、FormatExceptionがスローされます。
2018年

@alfoks:まったく正しいので、答えを更新しました。
Martin Liversage、2018年

1

あなたの要件が本当に

.ToString()は、数字と+、-、を含む文字列になります。

また、double.TryParseを使用する場合は、NumberStylesパラメータを受け取るオーバーロードを使用し、インバリアントカルチャを使用していることを確認する必要があります。

たとえば、先頭に符号があり、先頭または末尾に空白がなく、桁区切り記号とピリオド小数点区切り記号がない可能性がある数値の場合は、次のように使用します。

NumberStyles style = 
   NumberStyles.AllowLeadingSign | 
   NumberStyles.AllowDecimalPoint | 
double.TryParse(input, style, CultureInfo.InvariantCulture, out result);

1

object.IsNumeric()この質問に対するSaul Dolginの回答に基づいて独自の拡張メソッドを作成しているときOverflowExceptionに、double.MaxValueまたはで試すとが発生するという潜在的な問題が発生しましたdouble.MinValue

私の「解決策」は、Noldorinからの受け入れられた回答をSaul Dolginからの回答と組み合わせ、パターンマッチングスイッチを追加してから、何かを解析することです(C#7の優れた点を使用して少し整頓します)。

public static bool IsNumeric(this object obj)
{
    if (obj == null) return false;

    switch (obj)
    {
        case sbyte _: return true;
        case byte _: return true;
        case short _: return true;
        case ushort _: return true;
        case int _: return true;
        case uint _: return true;
        case long _: return true;
        case ulong _: return true;
        case float _: return true;
        case double _: return true;
        case decimal _: return true;
    }

    string s = Convert.ToString(obj, CultureInfo.InvariantCulture);

    return double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out double _);
}

0

はい、これでうまくいきます:

object x = 1;
Assert.That(x is int);

浮動小数点数の場合、float型を使用してテストする必要があります。

object x = 1f;
Assert.That(x is float);

これは、オブジェクトが暗黙的または明示的にオブジェクトにキャストされる前にintであった場合に機能します。あなたの例では、マジック番号1はintであり、変数xの型に暗黙的にキャストされます。オブジェクトx = 1.0を実行した場合、アサートはfalseを返します。
デクスター

intではない数値があります。
FredrikMörk、

はい、それで私のポイントは基本的に@Noldorinが彼の答えに今持っているものです。
Peter Lillevold、2009
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.