So analysieren Sie eine Zeichenfolge in ein nullbares int

300

Ich möchte einen String in C # in ein nullbares Int analysieren. dh. Ich möchte entweder den int-Wert des Strings oder null zurückgeben, wenn er nicht analysiert werden kann.

Ich hatte irgendwie gehofft, dass dies funktionieren würde

int? val = stringVal as int?;

Aber das wird nicht funktionieren. So wie ich es jetzt mache, habe ich diese Erweiterungsmethode geschrieben

public static int? ParseNullableInt(this string value)
{
    if (value == null || value.Trim() == string.Empty)
    {
        return null;
    }
    else
    {
        try
        {
            return int.Parse(value);
        }
        catch
        {
            return null;
        }
    }
}   

Gibt es einen besseren Weg, dies zu tun?

EDIT: Danke für die TryParse-Vorschläge, das wusste ich, aber es hat ungefähr genauso geklappt. Ich bin mehr daran interessiert zu wissen, ob es eine integrierte Framework-Methode gibt, die direkt in ein nullbares int analysiert wird.

Glenn Slaven
quelle
1
Sie können string.IsNullOrEmpty (Wert) verwenden, um die if-Zeile klarer zu machen.
Özgür Kaplan
Erwägen
Michael Freidgeim

Antworten:

352

int.TryParse ist wahrscheinlich ein bisschen einfacher:

public static int? ToNullableInt(this string s)
{
    int i;
    if (int.TryParse(s, out i)) return i;
    return null;
}

Edit @Glenn int.TryParseist "in das Framework eingebaut". Es int.Parseist der Weg, um Strings in Ints zu analysieren.

Matt Hamilton
quelle
82
eine Zeile weniger: Int32.TryParse (s, out i) zurückgeben? i: null;
Chris Shouts
2
"a" wird null zurückgeben, aber es ist nicht int und sollte eine Ausnahme
auslösen
54
@Chris, der Compiler mag Ihre Inline-if-Anweisung nicht (Diese Typen sind nicht kompatibel: 'int': 'null'). Ich musste es ändern, um: Int32.TryParse (s, out i) zurückzugeben? (int?) i: null;
Death_au
8
Int32 ist nur ein Alias ​​für int. Ich würde int.TryParse verwenden, um die verwendeten Typen in Ausrichtung zu halten. Wenn / wenn int verwendet wird, um eine andere Ganzzahl mit Bitlänge darzustellen (was passiert ist), stimmt Int32 nicht mit int überein.
Richard Collette
4
int.TryParse (s, out i) zurückgeben? (int?) i: null;
Nick Spreitzer
177

Sie können dies in einer Zeile tun, indem Sie den bedingten Operator und die Tatsache verwenden, dass Sie nullin einen nullbaren Typ umwandeln können (zwei Zeilen, wenn Sie kein bereits vorhandenes int haben, können Sie es für die Ausgabe von wiederverwenden TryParse):

Pre C # 7:

int tempVal;
int? val = Int32.TryParse(stringVal, out tempVal) ? Int32.Parse(stringVal) : (int?)null;

Mit der aktualisierten Syntax von C # 7, mit der Sie eine Ausgabevariable im Methodenaufruf deklarieren können, wird dies noch einfacher.

int? val = Int32.TryParse(stringVal, out var tempVal) ? tempVal : (int?)null;
McKenzieG1
quelle
4
Das hängt von Ihrer Ansicht des bedingten Operators ab, denke ich. Mein mentales Modell ist, dass es ziemlich viel syntaktischer Zucker für das Wenn-Sonst-Äquivalent ist. In diesem Fall sind meine Version und die von Matt nahezu identisch, wobei seine expliziter ist, meine mehr cmopact.
McKenzieG1
11
Hier gibt es keine Nebenwirkung in der Reihenfolge der Bewertung. Alle Schritte sind explizit angeordnet und korrekt.
Jon Hanna
22
Rückkehrint.TryParse(val, out i) ? i : default(int?);
Bart Calixto
7
@ Barts "Antwort" ist hier die beste!
Andre Figueiredo
4
Und jetzt im C # 6 kann es eine Zeile sein! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA
34

[ Aktualisiert , um modernes C # gemäß dem Vorschlag von @ sblom zu verwenden]

Ich hatte dieses Problem und endete damit (immerhin ist an ifund 2 returns so langatmig!):

int? ToNullableInt (string val)
    => int.TryParse (val, out var i) ? (int?) i : null;

Versuchen Sie im Ernst, nicht zu mischen int, was ein C # -Schlüsselwort ist, mit Int32, was ein .NET Framework-BCL-Typ ist - obwohl es funktioniert, lässt es Code nur chaotisch aussehen.

Duckboy
quelle
3
Ich
bin
1
Noch prägnanter in C # 7: Löschen Sie die int i;Zeile und gehen Sie einfach mitreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom
2
Also der Vollständigkeit int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
halber ;-)
Mit C # 6 kann dies auf 1 Zeile reduziert werden: return int.TryParse (Wert, out var result)? Ergebnis: (int?) null;
MeanGreen
16

Glenn Slaven : Ich bin mehr daran interessiert zu wissen, ob es eine integrierte Framework-Methode gibt, die direkt in ein nullbares int analysiert wird.

Es gibt diesen Ansatz, der direkt in ein nullbares int (und nicht nur in int) analysiert wird, wenn der Wert wie null oder eine leere Zeichenfolge gültig ist, aber eine Ausnahme für ungültige Werte auslöst, sodass Sie die Ausnahme abfangen und den Standardwert zurückgeben müssen für diese Situationen:

public static T Parse<T>(object value)
{
    try { return (T)System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(value.ToString()); }
    catch { return default(T); }
}

Dieser Ansatz kann weiterhin sowohl für nicht nullbare als auch für nullfähige Parses verwendet werden:

enum Fruit { Orange, Apple }
var res1 = Parse<Fruit>("Apple");
var res2 = Parse<Fruit?>("Banana");
var res3 = Parse<int?>("100") ?? 5; //use this for non-zero default
var res4 = Parse<Unit>("45%");

NB: Es gibt eine IsValid-Methode für den Konverter, die Sie verwenden können, anstatt die Ausnahme zu erfassen (ausgelöste Ausnahmen führen erwartungsgemäß zu unnötigem Overhead ). Leider funktioniert es erst seit .NET 4, aber es gibt immer noch ein Problem, bei dem Ihr Gebietsschema bei der Überprüfung der korrekten DateTime-Formate nicht überprüft wird (siehe Fehler 93559) .

Michael
quelle
Ich habe dies auf ganze Zahlen getestet und es ist viel langsamer als int.TryParse ((string) value, out var result)? Ergebnis: Standard (int?);
Wouter
12
var result = int.TryParse(foo, out var f) ? f : default(int?);

Quellen:

Jaa H.
quelle
Wie könnte das funktionieren? Tryparse funktioniert nicht oder nullbare Variablen und f in Ihrem Beispiel müsste nullbar sein.
John Lord
Bitte können Sie klarstellen, was Sie meinen @JohnLord
Jaa H
tryparse erwartet, dass es in eine nicht nullfähige Variable eingefügt wird. Würde Ihre Standardvariable (int?) nicht erzwingen, dass var nullbar ist?
John Lord
@ JohnLord Vielleicht hilft dir das zu verstehen, was auf stackoverflow.com/questions/3632918/…
Jaa H
9

Altes Thema, aber wie wäre es mit:

public static int? ParseToNullableInt(this string value)
{
     return String.IsNullOrEmpty(value) ? null : (int.Parse(value) as int?);
}

Ich mag dies besser als die Anforderung, wo Null analysiert werden soll, die TryParse-Version würde keinen Fehler auf zB ToNullableInt32 (XXX) werfen. Dies kann zu unerwünschten stillen Fehlern führen.

mortb
quelle
1
Genau das ist der Punkt - wenn die Zeichenfolge nicht analysiert werden kann int, sollte sie zurückkehren nullund keine Ausnahme auslösen.
Svick
1
Wenn der Wert nicht numerisch ist, löst int.Parse eine Ausnahme aus, die nicht mit der Rückgabe von null identisch ist.
Ein Phu
8

Versuche dies:

public static int? ParseNullableInt(this string value)
{
    int intValue;
    if (int.TryParse(value, out intValue))
        return intValue;
    return null;
}
Joseph Daigle
quelle
5

Ich denke, meine Lösung ist eine sehr saubere und schöne Lösung:

public static T? NullableParse<T>(string s) where T : struct
{
    try
    {
        return (T)typeof(T).GetMethod("Parse", new[] {typeof(string)}).Invoke(null, new[] { s });
    }
    catch (Exception)
    {
        return null;
    }
}

Dies ist natürlich eine generische Lösung, die nur erfordert, dass das generische Argument eine statische Methode "Parse (string)" hat. Dies funktioniert für Zahlen, Boolesche Werte, DateTime usw.

Lyskespark
quelle
5

Sie können alle anderen Antworten vergessen - es gibt eine großartige generische Lösung: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Auf diese Weise können Sie sehr sauberen Code wie folgt schreiben:

string value = null;
int? x = value.ConvertOrDefault();

und auch:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault();
bool? nullableBoolean = "".ConvertOrDefault();
int integer = obj.ConvertOrDefault();
int negativeInteger = "-12123".ConvertOrDefault();
int? nullableInteger = value.ConvertOrDefault();
MyEnum enumValue = "SecondValue".ConvertOrDefault();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault();
Pavel Hodek
quelle
1
Das ist in der Tat sehr nützlich. Meiner Meinung nach sollte dies in den Standard-C # -Bibliotheken sein, da Konvertierungen in jedem Programm sehr häufig sind;)
BigChief
Dies ist sehr schön und nützlich, ABER ich kann hinzufügen, dass es extrem langsam ist, wenn Konvertierungen für jedes Element in einer großen Sammlung von Elementen durchgeführt werden müssen. Ich habe mit 20000 Elementen getestet: Mit diesem Ansatz dauert das Konvertieren von 8 Eigenschaften jedes Elements bis zu 1 Stunde, um die gesamte Sammlung fertigzustellen. Bei gleichen Beispieldaten, jedoch unter Verwendung des Ansatzes von Matt Hamilton, dauert der Vorgang nur wenige Sekunden.
Zed
3

Das Folgende sollte für jeden Strukturtyp funktionieren. Es basiert auf Code von Matt Manela aus MSDN-Foren . Wie Murph betont, kann die Ausnahmebehandlung im Vergleich zur Verwendung der Typ-dedizierten TryParse-Methode teuer sein.

        public static bool TryParseStruct<T>(this string value, out Nullable<T> result)
            where T: struct 
        {
            if (string.IsNullOrEmpty(value))
            {
                result = new Nullable<T>();

                return true;
            }

            result = default(T);
            try
            {
                IConvertible convertibleString = (IConvertible)value;
                result = new Nullable<T>((T)convertibleString.ToType(typeof(T), System.Globalization.CultureInfo.CurrentCulture));
            }
            catch(InvalidCastException)
            {
                return false;
            }
            catch (FormatException)
            {
                return false;
            }

           return true;
        }

Dies waren die grundlegenden Testfälle, die ich verwendet habe.

        string parseOne = "1";
        int? resultOne;
        bool successOne = parseOne.TryParseStruct<int>(out resultOne);
        Assert.IsTrue(successOne);
        Assert.AreEqual(1, resultOne);

        string parseEmpty = string.Empty;
        int? resultEmpty;
        bool successEmpty = parseEmpty.TryParseStruct<int>(out resultEmpty);
        Assert.IsTrue(successEmpty);
        Assert.IsFalse(resultEmpty.HasValue);

        string parseNull = null;
        int? resultNull;
        bool successNull = parseNull.TryParseStruct<int>(out resultNull);
        Assert.IsTrue(successNull);
        Assert.IsFalse(resultNull.HasValue);

        string parseInvalid = "FooBar";
        int? resultInvalid;
        bool successInvalid = parseInvalid.TryParseStruct<int>(out resultInvalid);
        Assert.IsFalse(successInvalid);
Daniel Ballinger
quelle
3

Ich würde folgende Erweiterungsmethoden für das Parsen von Zeichenfolgen in int-Werte vorschlagen, mit denen der Standardwert definiert werden kann, falls das Parsen nicht möglich ist:

public static int ParseInt(this string value, int defaultIntValue = 0)
        {
            return int.TryParse(value, out var parsedInt) ? parsedInt : defaultIntValue;
        }

public static int? ParseNullableInt(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            return value.ParseInt();
        }
Aleksandr Neizvestnyi
quelle
Es gibt bereits so viele und sogar hoch bewertete Antworten. Glaubst du wirklich, dass deine Antwort benötigt wird und fügt diesem Beitrag neue Qualität hinzu?
L. Guthardt
1
@ L.Guthardt Ja, ich denke schon. Da ich denke, dass meine Antwort einen universelleren Weg zur Lösung des fraglichen Problems bietet. Danke dir.
Aleksandr Neizvestnyi
2

Diese Lösung ist generisch ohne Reflexionsaufwand.

public static Nullable<T> ParseNullable<T>(string s, Func<string, T> parser) where T : struct
{
    if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(s.Trim())) return null;
    else return parser(s);
}

static void Main(string[] args)
{
    Nullable<int> i = ParseNullable("-1", int.Parse);
    Nullable<float> dt = ParseNullable("3.14", float.Parse);
}
Qi Luo
quelle
Ich glaube , Sie können ersetzen IsNullOrEmptymitIsNullOrWhitespace
NibblyPig
1

Ich bin mehr daran interessiert zu wissen, ob es eine integrierte Framework-Methode gibt, die direkt in ein nullbares int analysiert wird.

Das gibt es nicht.

Orion Edwards
quelle
1
Würden Sie dies als direkten Ansatz betrachten? stackoverflow.com/a/6474962/222748
Michael
1

Ich hatte das Gefühl, ich sollte meine teilen, was etwas allgemeiner ist.

Verwendungszweck:

var result = "123".ParseBy(int.Parse);

var result2 = "123".ParseBy<int>(int.TryParse);

Lösung:

public static class NullableParse
{
    public static Nullable<T> ParseBy<T>(this string input, Func<string, T> parser)
        where T : struct
    {
        try
        {
            return parser(input);
        }
        catch (Exception exc)
        {
            return null;
        }
    }

    public delegate bool TryParseDelegate<T>(string input, out T result);

    public static Nullable<T> ParseBy<T>(this string input, TryParseDelegate<T> parser)
        where T : struct
    {
        T t;
        if (parser(input, out t)) return t;
        return null;
    }
}

Die erste Version ist langsamer, da sie einen Try-Catch erfordert, aber sauberer aussieht. Wenn es nicht oft mit ungültigen Zeichenfolgen aufgerufen wird, ist es nicht so wichtig. Wenn die Leistung ein Problem darstellt, beachten Sie bitte, dass Sie bei Verwendung von TryParse-Methoden den Typparameter von ParseBy angeben müssen, da dieser vom Compiler nicht abgeleitet werden kann. Ich musste auch einen Delegaten definieren, da unser Schlüsselwort out nicht in Func <> verwendet werden kann, aber zumindest diesmal benötigt der Compiler keine explizite Instanz.

Schließlich können Sie es auch mit anderen Strukturen verwenden, z. B. Dezimal, DateTime, Guid usw.

Orcun
quelle
1

Ich habe Code für eine generische NullableParser-Klasse gefunden und angepasst. Der vollständige Code befindet sich in meinem Blog Nullable TryParse

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace SomeNamespace
{
    /// <summary>
    /// A parser for nullable types. Will return null when parsing fails.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    ///
    public static class NullableParser<T> where T : struct
    {
        public delegate bool TryParseDelegate(string s, out T result);
        /// <summary>
        /// A generic Nullable Parser. Supports parsing of all types that implements the tryParse method;
        /// </summary>
        /// <param name="text">Text to be parsed</param>
        /// <param name="result">Value is true for parse succeeded</param>
        /// <returns>bool</returns>
        public static bool TryParse(string s, out Nullable<T> result)
        {
            bool success = false;
            try
            {
                if (string.IsNullOrEmpty(s))
                {
                    result = null;
                    success = true;
                }
                else
                {
                    IConvertible convertableString = s as IConvertible;
                    if (convertableString != null)
                    {
                        result = new Nullable<T>((T)convertableString.ToType(typeof(T),
                            CultureInfo.CurrentCulture));
                        success = true;
                    }
                    else
                    {
                        success = false;
                        result = null;
                    }
                }
            }
            catch
            {
                success = false;
                result = null;
            }
            return success;
        }
    }
}
John Dauphine
quelle
1
404 Nicht gefunden. Es ist keine gute Praxis, nur einen Link zu geben
Dirty-Flow
Entschuldigung für das @ Dirty-Flow-Update mit vollständigem Code. Besser spät als nie :)
John Dauphine
1
    public static void Main(string[] args)
    {

        var myString = "abc";

        int? myInt = ParseOnlyInt(myString);
        // null

        myString = "1234";

        myInt = ParseOnlyInt(myString);
        // 1234
    }
    private static int? ParseOnlyInt(string s)
    {
        return int.TryParse(s, out var i) ? i : (int?)null;
    }
Crivelli
quelle
1
Wenn myString nicht numerisch ist, löst int.Parse eine Ausnahme aus, die nicht mit der Rückgabe von null identisch ist.
Ein Phu
0

Sie sollten niemals eine Ausnahme verwenden, wenn Sie nicht müssen - der Overhead ist schrecklich.

Die Variationen von TryParse lösen das Problem - wenn Sie kreativ werden möchten (um Ihren Code eleganter aussehen zu lassen), könnten Sie wahrscheinlich etwas mit einer Erweiterungsmethode in 3.5 tun, aber der Code wäre mehr oder weniger der gleiche.

Murph
quelle
0

Mithilfe von Delegaten kann der folgende Code die Wiederverwendbarkeit gewährleisten, wenn Sie die nullbare Analyse für mehr als einen Strukturtyp benötigen. Ich habe hier sowohl die Versionen .Parse () als auch .TryParse () gezeigt.

Dies ist eine Beispielverwendung:

NullableParser.TryParseInt(ViewState["Id"] as string);

Und hier ist der Code, der Sie dorthin bringt ...

public class NullableParser
  {
    public delegate T ParseDelegate<T>(string input) where T : struct;
    public delegate bool TryParseDelegate<T>(string input, out T outtie) where T : struct;
    private static T? Parse<T>(string input, ParseDelegate<T> DelegateTheParse) where T : struct
    {
      if (string.IsNullOrEmpty(input)) return null;
      return DelegateTheParse(input);
    }
    private static T? TryParse<T>(string input, TryParseDelegate<T> DelegateTheTryParse) where T : struct
    {
      T x;
      if (DelegateTheTryParse(input, out x)) return x;
      return null;
    }
    public static int? ParseInt(string input)
    {
      return Parse<int>(input, new ParseDelegate<int>(int.Parse));
    }
    public static int? TryParseInt(string input)
    {
      return TryParse<int>(input, new TryParseDelegate<int>(int.TryParse));
    }
    public static bool? TryParseBool(string input)
    {
      return TryParse<bool>(input, new TryParseDelegate<bool>(bool.TryParse));
    }
    public static DateTime? TryParseDateTime(string input)
    {
      return TryParse<DateTime>(input, new TryParseDelegate<DateTime>(DateTime.TryParse));
    }
  }
umbyersw
quelle
0

Mir ist klar, dass dies ein altes Thema ist, aber können Sie nicht einfach:

(Nullable<int>)int.Parse(stringVal);

?

Leigh Bowers
quelle
Sie können, aber dann erhalten Sie eine Ausnahme, wenn stringVal das falsche Format hat. Siehe die int.Parse-Dokumentation: msdn.microsoft.com/en-us/library/b3h1hf19.aspx
Alex
0

Ich habe mir diese ausgedacht, die meine Anforderungen erfüllt hat (ich wollte, dass meine Erweiterungsmethode die Rückgabe von TryParse des Frameworks so nah wie möglich emuliert, aber ohne try {} catch {} -Blöcke und ohne dass sich der Compiler über das Ableiten von a beschwert nullbarer Typ innerhalb der Framework-Methode)

private static bool TryParseNullableInt(this string s, out int? result)
{
    int i;
    result = int.TryParse(s, out i) ? (int?)i : null;
    return result != null;
}
wmoecke
quelle
0

Ich schlage Code unten vor. Sie können mit Ausnahme arbeiten, wenn ein Konvertierungsfehler aufgetreten ist.

public static class Utils {      
public static bool TryParse<Tin, Tout>(this Tin obj, Func<Tin, Tout> onConvert, Action<Tout> onFill, Action<Exception> onError) {
  Tout value = default(Tout);
  bool ret = true;
  try {
    value = onConvert(obj);
  }
  catch (Exception exc) {
    onError(exc);
    ret = false;
  }
  if (ret)
    onFill(value);
  return ret;
}

public static bool TryParse(this string str, Action<int?> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => string.IsNullOrEmpty(s) ? null : (int?)int.Parse(s)
    , onFill
    , onError);
}
public static bool TryParse(this string str, Action<int> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => int.Parse(s)
    , onFill
    , onError);
}
}

Verwenden Sie diese Erweiterungsmethode im Code (füllen Sie die Eigenschaft int? Age einer Personenklasse aus):

string ageStr = AgeTextBox.Text;
Utils.TryParse(ageStr, i => person.Age = i, exc => { MessageBox.Show(exc.Message); });

ODER

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
lison
quelle