Überprüfen, ob ein Objekt eine Zahl in C # ist

89

Ich möchte überprüfen , ob ein Objekt eine Zahl ist , so dass .ToString()in einem String führen würde enthalten Ziffern und +, -,.

Ist es durch einfaches Typprüfen in .net möglich (wie :) if (p is Number)?

Oder sollte ich in einen String konvertieren und dann versuchen, ihn zu verdoppeln?

Update: Um zu verdeutlichen, dass mein Objekt int, uint, float, double usw. ist, ist es keine Zeichenfolge. Ich versuche eine Funktion zu erstellen, die jedes Objekt wie folgt in XML serialisiert:

<string>content</string>

oder

<numeric>123.3</numeric>

oder eine Ausnahme auslösen.

Piotr Czapla
quelle
5
Klingt so, als würden Sie versuchen, Ihren eigenen XmlSerializer zu schreiben. Was ist mit dem einen Anbieter von .NET- msdn.microsoft.com/en-us/library/… falsch ?
RichardOD
2
Möglicherweise können Sie dieses Problem umgehen, indem Sie Ihr XML-Format mithilfe einer XSD definieren und dann ein Objekt erstellen, in das Sie Ihre Daten mithilfe des mitgelieferten
Dexter
@RichardOD: Kann ich die XML-Serialisierung verwenden, um das Objekt [] zu serialisieren? Ich brauche es, um die Flash-Funktion adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/… aufzurufen
Piotr Czapla

Antworten:

180

Sie müssen lediglich eine Typprüfung für jeden der numerischen Grundtypen durchführen.

Hier ist eine Erweiterungsmethode, die den Job erledigen sollte:

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

Dies sollte alle numerischen Typen abdecken.

Aktualisieren

Es scheint, dass Sie die Nummer während der Deserialisierung tatsächlich aus einer Zeichenfolge analysieren möchten. In diesem Fall wäre es wahrscheinlich am besten zu verwenden double.TryParse.

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

Natürlich würde dies nicht mit sehr großen ganzen Zahlen / langen Dezimalstellen umgehen, aber wenn dies der Fall ist, müssen Sie nur zusätzliche Aufrufe zu long.TryParse/ decimal.TryParse/ was auch immer hinzufügen .

Noldorin
quelle
Mein Objekt ist int, short, uint, float, double oder irgendetwas anderes, das eine Zahl ist
Piotr Czapla
@Piotr: Ah richtig. Es scheint, ich habe dich missverstanden. Siehe meine aktualisierte Antwort.
Noldorin
1
@Noldorin: Eigentlich würde auch deine vorherige Version des Codes funktionieren; Fügen Sie einfach eine Nullprüfung hinzu und verwenden Sie value.ToString (). Dann müssen Sie nicht nach allen numerischen Typen suchen.
Fredrik Mörk
1
@Noldorin Es ist traurig, dass es die richtige Lösung ist, die ausführlich :(.
Piotr Czapla
1
@ Joe: Eigentlich würde es keinen Unterschied machen, da ToString auch die aktuelle Kultur verwenden würde.
Noldorin
36

Entnommen aus Scott Hanselmans Blog :

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);
}
Saul Dolgin
quelle
6
Das Problem bei diesem Ansatz ist, dass wenn Sie eine Zeichenfolge übergeben, die wie eine Zahl aussieht, diese formatiert wird. Könnte für die meisten Leute in Ordnung sein, aber es war ein Show Stopper für mich.
Rob Sedgwick
1
Ein weiteres potenzielles Problem dabei ist, dass Sie die Min / Max-Werte nicht für double analysieren können. double.Parse(double.MaxValue.ToString())verursacht eine OverflowException. Sie können dies beheben, indem Sie .ToString("R")in diesem Fall den Roundtrip-Modifikator angeben. Diese Überlastung ist jedoch nicht verfügbar, Convert.ToString(...)da wir den Typ nicht kennen. Ich weiß, dass dies ein Randfall ist, aber ich bin darauf gestoßen, als ich Tests für meine eigene .IsNumeric()Erweiterung geschrieben habe. Meine "Lösung" bestand darin, eine Typprüfung hinzuzufügen, bevor ich versuchte, etwas zu analysieren. Den Code finden Sie in meiner Antwort auf diese Frage.
Ben
21

Nutzen Sie die IsPrimitive-Eigenschaft, um eine praktische Erweiterungsmethode zu erstellen:

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

BEARBEITEN: Gemäß Kommentaren behoben. Die Generika wurden entfernt, da die Werttypen .GetType () Felder sind. Ebenfalls enthaltener Fix für nullbare Werte.

Kenan EK
quelle
1
Der Generika-Teil gibt Ihnen hier kein Extra, oder? Sie greifen nur auf GetType () zu, das für das Objekt verfügbar ist ...
Peter Lillevold
Es wird eine Box-Operation gespeichert, wenn ein Werttyp aufgerufen wird. Denken Sie an Wiederverwendbarkeit.
Kenan EK
1
Warum nicht typeof (T) anstelle von obj.GetType verwenden? Auf diese Weise erhalten Sie keine NullReferenceException, wenn jemand einen Nullreferenztyp übergibt. Sie können T auch generisch einschränken, um nur Werttypen zu akzeptieren. Natürlich haben Sie beim Kompilieren viele Informationen, wenn Sie dies tun.
Trillian
objectund stringsind keine primitiven Typen.
Wir sind alle Monica
@jnylen: Diese Antwort ist schon einige Zeit her. Ich glaube, ich habe damals etwas aus einer reflektierten Framework-Quelle ausgegraben, aber wer kann das heute sagen ... Feste Antwort.
Kenan EK
10

Es gibt einige gute Antworten oben. Hier ist eine All-in-One-Lösung. Drei Überlastungen für unterschiedliche Umstände.

// 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)); }
Mick Bruno
quelle
Erwägen Sie das
Keine Nullprüfung erforderlich - als Erweiterungsmethode können Sie sie nicht mit einem Nullwert aufrufen. Natürlich könnte jemand immer noch als normale Funktion aufrufen, aber das ist nicht die erwartete Verwendung einer Erweiterungsmethode.
Mick Bruno
5
Ich denke, man kann es mit Nullwert nennen. Objekt obj = null; obj.IsNumeric ();
Wiero
Danke Weiro, habe es behoben. Ich wusste nicht, dass das Aufrufen der Erweiterungsmethode mit einem Nullwert möglich ist, aber das ist es natürlich!
Mick Bruno
Ich denke, der ersten Überladung fehlt eine Klammer am Ende: "return (x == null? False: IsNumeric (x.GetType ()));"
Glenn Garson
8

Anstatt Ihre eigenen zu rollen, ist die zuverlässigste Methode, um festzustellen, ob ein eingebauter Typ numerisch ist, wahrscheinlich das Referenzieren Microsoft.VisualBasicund Aufrufen Information.IsNumeric(object value). Die Implementierung behandelt eine Reihe subtiler Fälle wie char[]HEX- und OCT-Zeichenfolgen.

Satnhak
quelle
Dies sollte oben sein!
Nawfal
4

Dort gibt es drei verschiedene Konzepte:

  • Um zu überprüfen, ob es sich um eine Zahl handelt (dh um einen (normalerweise mit einem Kästchen versehenen) numerischen Wert selbst), überprüfen Sie den Typ beispielsweise mit is-if(obj is int) {...}
  • um zu überprüfen, ob eine Zeichenfolge als Zahl analysiert werden kann; verwendenTryParse()
  • Wenn das Objekt jedoch keine Zahl oder Zeichenfolge ist, Sie jedoch den Verdacht haben ToString(), dass es etwas gibt, das wie eine Zahl aussieht , rufen Sie es auf ToString() und behandeln Sie es als Zeichenfolge

In beiden ersten beiden Fällen müssen Sie wahrscheinlich jeden zu unterstützenden numerischen Typ ( double/ decimal/ int) separat behandeln - jeder hat beispielsweise unterschiedliche Bereiche und Genauigkeiten.

Sie können sich auch Regex ansehen, um eine schnelle grobe Überprüfung zu erhalten.

Marc Gravell
quelle
4

Angenommen, Ihre Eingabe ist eine Zeichenfolge ...

Es gibt zwei Möglichkeiten:

benutze Double.TryParse ()

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

Verwenden Sie Regex

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

Sie könnten folgenden Code verwenden:

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

Wenn Ihr Objekt eine ist Int32, Single, Doubleusw. wird die Konvertierung durchführen. Ein String wird ebenfalls implementiert, IConvertibleaber wenn der String nicht in ein Double konvertierbar ist, FormatExceptionwird ein geworfen.

Martin Liversage
quelle
Tatsächlich werden Zeichenfolgen analysiert, aber wenn sie nicht im richtigen Format vorliegen, wird FormatException ausgelöst.
Alfoks
@alfoks: Du hast absolut Recht, also habe ich die Antwort aktualisiert.
Martin Liversage
1

Wenn Ihre Anforderung wirklich ist

.ToString () würde zu einer Zeichenfolge führen, die Ziffern und +, -, enthält.

Wenn Sie double.TryParse verwenden möchten, müssen Sie die Überladung verwenden, die einen NumberStyles-Parameter verwendet, und sicherstellen, dass Sie die invariante Kultur verwenden.

Verwenden Sie beispielsweise für eine Zahl, die möglicherweise ein führendes Vorzeichen, kein führendes oder nachfolgendes Leerzeichen, kein Tausendertrennzeichen und ein Dezimaltrennzeichen enthält:

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

Beim Schreiben meiner eigenen object.IsNumeric()Erweiterungsmethode basierend auf der Antwort von Saul Dolgin auf diese Frage bin ich auf ein potenzielles Problem gestoßen, bei dem Sie eine erhalten, OverflowExceptionwenn Sie es mit double.MaxValueoder versuchen double.MinValue.

Meine "Lösung" bestand darin, die akzeptierte Antwort von Noldorin mit der von Saul Dolgin zu kombinieren und einen Mustervergleichsschalter hinzuzufügen, bevor ich versuchte, etwas zu analysieren (und etwas C # 7-Güte zu verwenden, um ein bisschen aufzuräumen):

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 _);
}
Ben
quelle
0

Ja, das funktioniert:

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

Für eine Gleitkommazahl müssten Sie mit dem Gleitkommatyp testen:

object x = 1f;
Assert.That(x is float);
Peter Lillevold
quelle
Dies funktioniert, wenn das Objekt ein int war, bevor es implizit oder explizit in ein Objekt umgewandelt wurde. In Ihrem Beispiel ist die magische Zahl 1 ein int und wird dann implizit in den Typ der Variablen x umgewandelt. Wenn Sie das Objekt x = 1.0 ausgeführt hätten, hätte Ihre Behauptung false zurückgegeben.
Dexter
Es gibt Zahlen, die keine Ints sind.
Fredrik Mörk
Ja, mein Punkt ist also im Grunde das, was @Noldorin gerade in seiner Antwort hat.
Peter Lillevold