Jak parsować ciąg w int nullable int


300

Chcę parsować ciąg do nullable int w C #. to znaczy. Chcę odzyskać wartość int łańcucha lub wartość null, jeśli nie można go przeanalizować.

Miałem nadzieję, że to zadziała

int? val = stringVal as int?;

Ale to nie zadziała, więc teraz robię to, że napisałem tę metodę rozszerzenia

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

Czy jest na to lepszy sposób?

EDYCJA: Dzięki za sugestie TryParse, wiedziałem o tym, ale działało to tak samo. Bardziej interesuje mnie wiedza, czy istnieje wbudowana metoda frameworka, która będzie przetwarzać bezpośrednio w nullable int?


1
Możesz użyć string.IsNullOrEmpty (wartość), aby uzyskać wyraźniejszy wiersz if.
Özgür Kaplan

Rozważ skorzystanie z konwersji generics stackoverflow.com/questions/773078/...
Michael Freidgeim

Odpowiedzi:


352

int.TryParse jest chyba odrobinę łatwiejsze:

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

Edycja @Glenn int.TryParsejest „wbudowana w środowisko”. To i int.Parsesą sposób analizowania ciągi do wskazówki.


82
jedna linia mniej: zwraca Int32.TryParse (s, out i)? i: zero;
Chris Shouts

2
„a” zwróci null, ale nie jest to int i powinno wyrzucić wyjątek
Arsen Mkrtchyan

54
@Chris, kompilatorowi nie podoba się twoja instrukcja inline if (te typy nie są kompatybilne: 'int': 'null'). Musiałem to zmienić do: return Int32.TryParse (s, out i)? (int?) i: null;
death_au

8
Int32 to tylko alias int. Użyłbym int.TryParse, aby zachować typy używane w wyrównaniu. Jeśli / kiedy int jest używane do reprezentowania liczb całkowitych o różnych długościach bitów (co się zdarzyło), Int32 nie będzie się zgadzać z int.
Richard Collette

4
return int.TryParse (s, out i)? (int?) i: null;
Nick Spreitzer,

177

Możesz to zrobić w jednym wierszu, używając operatora warunkowego i faktu, że możesz rzutować nullna typ zerowalny (dwa wiersze, jeśli nie masz wcześniej istniejącej zmiennej int, możesz użyć jej ponownie TryParse):

Przed C # 7:

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

Dzięki zaktualizowanej składni C # 7, która pozwala zadeklarować zmienną wyjściową w wywołaniu metody, staje się to jeszcze prostsze.

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

4
To zależy od twojego zdania na temat operatora warunkowego. Mój model mentalny jest taki, że jest to właściwie cukier składniowy dla ekwiwalentu if-else, w którym to przypadku moja wersja i Matt są prawie identyczne, a jego bardziej wyraźny, mój więcej cmopact.
McKenzieG1

11
Nie ma tutaj efektu ubocznego kolejności oceny. Wszystkie kroki są wyraźnie uporządkowane i poprawne.
Jon Hanna

22
powrótint.TryParse(val, out i) ? i : default(int?);
Bart Calixto,

7
„Odpowiedź” Barta jest tutaj najlepsza!
Andre Figueiredo,

4
A teraz w C # 6 może być jedna linia! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA

34

[ Zaktualizowano, aby używać nowoczesnego języka C # zgodnie z sugestią @ sblom]

Miałem ten problem i skończyłem z tym (w końcu, ifa 2 returns jest zbyt długa!):

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

Mówiąc poważniej, staraj się nie mieszać int, który jest słowem kluczowym C #, z Int32typem BCL .NET Framework - chociaż działa, to po prostu sprawia, że ​​kod wygląda na niechlujny.


3
Nie jestem pewien, czy to właściwie przełoży się na wszystko, co po kompilacji działa lepiej
BuZz

1
Jeszcze bardziej zwięzłe w C # 7: usuń int i;linię i po prostu idź zreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
Tak dla kompletności ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

Z C # 6 można to sprowadzić do 1 linii: return int.TryParse (wartość, wynik var var)? wynik: (int?) null;
MeanGreen

16

Glenn Slaven : Bardziej interesuje mnie informacja, czy istnieje wbudowana metoda frameworka, która będzie przetwarzać bezpośrednio w nullable int?

Istnieje takie podejście, które będzie analizować bezpośrednio do zerowalnej wartości int (a nie tylko int), jeśli wartość jest poprawna, jak null lub pusty ciąg, ale zgłasza wyjątek dla niepoprawnych wartości, więc trzeba będzie złapać wyjątek i zwrócić wartość domyślną w takich sytuacjach:

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

To podejście może być nadal stosowane w przypadku analiz składni, które nie mają wartości NULL, a także wartości NULL:

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%");

Uwaga: W konwerterze istnieje metoda IsValid, której można użyć zamiast przechwytywania wyjątku (zgłoszone wyjątki powodują niepotrzebne koszty ogólne, jeśli się tego spodziewają). Niestety działa tylko od .NET 4, ale nadal występuje problem polegający na tym, że nie sprawdza on ustawień regionalnych podczas sprawdzania poprawności formatów DateTime, patrz błąd 93559 .


Przetestowałem to dla liczb całkowitych i jest znacznie wolniejsze niż int.TryParse ((string) wartość, wynik var var)? wynik: domyślny (int?);
Wouter

12
var result = int.TryParse(foo, out var f) ? f : default(int?);

Źródła:


jak to może działać? Tryparse nie zadziała lub zmienne zerowalne, aw twoim przykładzie musiałoby być zerowalne.
John Lord,

Czy możesz wyjaśnić, co masz na myśli @JohnLord
Jaa H

tryparse spodziewa się, że zostanie wstawiona do zmiennej, która nie ma wartości zerowej, więc czy domyślna (int?) siła var nie będzie dopuszczalna?
John Lord

@JohnLord może to pomoże ci zrozumieć, co się dzieje na stackoverflow.com/questions/3632918/…
Jaa H

9

Stary temat, ale co powiesz na:

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

Podoba mi się to bardziej jako wymaganie, gdzie do parsowania null, wersja TryParse nie rzucałaby błędu np. Na ToNullableInt32 (XXX). Może to powodować niechciane ciche błędy.


1
Właśnie o to chodzi - jeśli łańcuch nie może zostać przeanalizowany int, powinien powrócić null, a nie zgłosić wyjątek.
sick

1
jeśli wartość nie jest liczbą, int.Parse zgłasza wyjątek, który nie jest tym samym, co zwracanie wartości null.
phu

8

Spróbuj tego:

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

5

Wydaje mi się, że moje rozwiązanie jest bardzo czyste i ładne:

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

Jest to oczywiście ogólne rozwiązanie, które wymaga tylko, aby argument ogólny miał statyczną metodę „Analiza składni (ciąg)”. Działa to dla liczb, wartości logicznych, daty i godziny itp.


5

Możesz zapomnieć o wszystkich innych odpowiedziach - istnieje świetne ogólne rozwiązanie: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Pozwala to napisać bardzo czysty kod w następujący sposób:

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

i również:

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

1
To jest rzeczywiście bardzo przydatne. Moim zdaniem powinno to znajdować się w standardowych bibliotekach c #, ponieważ konwersje są bardzo powszechne w każdym programie;)
BigChief 28.04.2013

Jest to bardzo miłe i przydatne, ALE mogę dodać, że jest bardzo powolny, gdy trzeba dokonać konwersji dla każdego elementu w dużej kolekcji elementów. Testowałem z 20000 pozycji: stosując to podejście, przekształcenie 8 właściwości każdego elementu zajmuje do 1 godziny, aby zakończyć całą kolekcję. Przy tych samych przykładowych danych, ale stosując podejście Matta Hamiltona, ukończenie zajmuje tylko kilka sekund.
Zed

3

Poniższe powinno działać dla każdego typu struktury. Jest oparty na kodzie Matta Maneli z forów MSDN . Jak zauważa Murph, obsługa wyjątków może być kosztowna w porównaniu z użyciem dedykowanej metody TryParse typu.

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

To były podstawowe przypadki testowe, których użyłem.

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

3

Sugerowałbym zastosowanie następujących metod rozszerzenia do parsowania łańcucha na wartość int z możliwością zdefiniowania wartości domyślnej w przypadku, gdy parsowanie nie jest możliwe:

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

Jest już tak wiele, a nawet wysoko ocenianych odpowiedzi. Czy naprawdę uważasz, że Twoja odpowiedź jest potrzebna i dodaje nowej jakości do tego postu?
L. Guthardt,

1
@ L.Guthardt Tak, tak mi się wydaje. Myślę, że moja odpowiedź stanowi bardziej uniwersalny sposób rozwiązania opisanego problemu. Dziękuję Ci.
Aleksandr Neizvestnyi

2

To rozwiązanie jest ogólne bez refleksji.

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

Myślę, że można zastąpić IsNullOrEmptyzIsNullOrWhitespace
NibblyPig


1

Czułem, że powinienem podzielić się moim, który jest nieco bardziej ogólny.

Stosowanie:

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

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

Rozwiązanie:

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

Pierwsza wersja jest wolniejsza, ponieważ wymaga próby, ale wygląda na czystszą. Jeśli nie zostanie wywołany wiele razy z niepoprawnymi łańcuchami, nie jest to takie ważne. Jeśli wydajność stanowi problem, należy pamiętać, że podczas korzystania z metod TryParse należy określić parametr typu ParseBy, ponieważ kompilator nie może wywnioskować. Musiałem także zdefiniować delegata, ponieważ słowa kluczowego out nie można używać w Func <>, ale przynajmniej tym razem kompilator nie wymaga jawnej instancji.

Wreszcie możesz używać go również z innymi strukturami, np. Dziesiętnymi, DateTime, Guid itp.


1

Znalazłem i zaadaptowałem trochę kodu dla ogólnej klasy NullableParser. Pełny kod znajduje się na moim blogu 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;
        }
    }
}

1
404 Nie Znaleziono. podanie linku nie jest dobrą praktyką
Dirty-flow

przepraszam za aktualizację @ Dirty-flow z pełnym kodem. Lepiej późno niż wcale :)
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;
    }

1
jeśli myString nie jest liczbą, int.Parse zgłasza wyjątek, który nie jest tym samym, co zwracanie wartości null.
phu

0

Nigdy nie powinieneś używać wyjątku, jeśli nie musisz - koszty ogólne są okropne.

Odmiany w TryParse rozwiązują problem - jeśli chcesz być kreatywny (aby Twój kod wyglądał bardziej elegancko), możesz prawdopodobnie zrobić coś z metodą rozszerzenia w wersji 3.5, ale kod byłby mniej więcej taki sam.


0

Korzystając z delegatów, poniższy kod jest w stanie zapewnić możliwość ponownego użycia, jeśli okaże się, że potrzebujesz parsowania dla więcej niż jednego typu struktury. Pokazałem tutaj zarówno wersje .Parse (), jak i .TryParse ().

To jest przykładowe użycie:

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

A oto kod, który Cię tam prowadzi ...

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


0

Wymyśliłem ten, który spełnił moje wymagania (chciałem, aby moja metoda rozszerzenia naśladowała jak najbliższy zwrot TryParse frameworka, ale bez bloków try {} catch {} i bez kompilatora narzekającego na wnioskowanie o typ dopuszczalny w ramach metody ramowej)

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

0

Sugeruję poniższy kod. Możesz pracować z wyjątkiem, gdy wystąpił błąd konwersji.

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

Użyj tej metody rozszerzenia w kodzie (wypełnij int? Właściwość Age klasy osoby):

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

LUB

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.