Come analizzare una stringa in un int nullable


300

Voglio analizzare una stringa in un int nullable in C #. vale a dire. Voglio recuperare il valore int della stringa o null se non può essere analizzato.

Speravo in qualche modo che funzionasse

int? val = stringVal as int?;

Ma non funzionerà, quindi il modo in cui lo sto facendo ora è che ho scritto questo metodo di estensione

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

C'è un modo migliore per farlo?

EDIT: Grazie per i suggerimenti TryParse, lo sapevo, ma ha funzionato allo stesso modo. Sono più interessato a sapere se esiste un metodo framework incorporato che analizzerà direttamente in un int nullable?


1
È possibile utilizzare string.IsNullOrEmpty (valore) per rendere più chiara la riga if.
Özgür Kaplan,

Prendere in considerazione di utilizzare la conversione generici stackoverflow.com/questions/773078/...
Michael Freidgeim

Risposte:


352

int.TryParse è probabilmente un po 'più semplice:

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

Modifica @Glenn int.TryParseè "integrato nel framework". Si e int.Parsesono il modo per analizzare le stringhe a interi.


82
una riga in meno: restituisce Int32. TryParse (s, out i)? i: null;
Chris Shouts,

2
"a" restituirà null, ma non è int e dovrebbe generare un'eccezione
Arsen Mkrtchyan,

54
@Chris, al compilatore non piace la tua istruzione if inline (questi tipi non sono compatibili: 'int': 'null'). Ho dovuto modificarlo in: return Int32.TryParse (s, out i)? (int?) i: null;
death_au,

8
Int32 è solo un alias di int. Vorrei usare int. TryParse per mantenere i tipi utilizzati in allineamento. Se / quando int viene utilizzato per rappresentare un intero di lunghezza bit diversa (che è successo), Int32 non si allinea con int.
Richard Collette,

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

178

Puoi farlo in una riga, usando l'operatore condizionale e il fatto che puoi eseguire il cast nullin un tipo nullable (due righe, se non hai un int preesistente puoi riutilizzarlo per l'output di TryParse):

Pre C # 7:

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

Con la sintassi aggiornata di C # 7 che consente di dichiarare una variabile di output nella chiamata del metodo, questo diventa ancora più semplice.

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

4
Dipende dalla tua visione dell'operatore condizionale, credo. Il mio modello mentale è che è praticamente zucchero sintattico per l'equivalente if-else, nel qual caso la mia versione e quella di Matt sono quasi identiche, con il suo essere più esplicito, il mio più cmopact.
McKenzieG1

11
Non ci sono effetti collaterali dell'ordine di valutazione qui. Tutti i passaggi sono esplicitamente ordinati e corretti.
Jon Hanna,

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

7
La "risposta" di Bart è la migliore qui!
Andre Figueiredo,

4
E ora nel C # 6, può essere una riga! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA,

34

[ Aggiornato per usare il moderno C # secondo il suggerimento di @ sblom]

Ho avuto questo problema e ho finito con questo (dopo tutto, un ife 2 returns è così prolisso!):

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

Su una nota più seria, prova a non mescolare int, che è una parola chiave C #, con Int32, che è un tipo BCL di .NET Framework - sebbene funzioni, rende solo il codice disordinato.


3
Non sono sicuro che questo si tradurrà in qualcosa che si comporta meglio una volta compilato
BuZz

1
Ancora più conciso in C # 7: elimina la int i;riga e vai avanti conreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom il

2
Quindi per completezza ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy,

Con C # 6 questo può essere ridotto a 1 riga: return int. TryParse (value, out var result)? risultato: (int?) null;
MeanGreen

16

Glenn Slaven : Sono più interessato a sapere se esiste un metodo framework incorporato che analizzerà direttamente in un int nullable?

Esiste questo approccio che analizzerà direttamente un int nullable (e non solo int) se il valore è valido come stringa nulla o vuota, ma genera un'eccezione per valori non validi, quindi sarà necessario catturare l'eccezione e restituire il valore predefinito per quelle situazioni:

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

Questo approccio può ancora essere utilizzato per analisi non annullabili e nullable:

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: Esiste un metodo IsValid sul convertitore che è possibile utilizzare invece di catturare l'eccezione (le eccezioni generate comportano un sovraccarico non necessario se previsto). Sfortunatamente funziona solo da .NET 4 ma c'è ancora un problema in cui non controlla le impostazioni locali durante la convalida dei formati DateTime corretti, vedi bug 93559 .


Ho provato questo per numeri interi ed è molto più lento di int.TryParse (valore (stringa), risultato var diverso)? risultato: default (int?);
Wouter,

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

fonti:


come potrebbe funzionare? Tryparse non funzionerà o variabili nullable e f nel tuo esempio dovrebbe essere nullable.
John Lord,

Per favore, puoi chiarire cosa intendi con @JohnLord
Jaa H

tryparse prevede di essere inserito in una variabile non nullable, quindi il valore predefinito (int?) force var sarà nullable?
John Lord,

@JohnLord forse questo ti aiuterà a capire cosa sta succedendo su stackoverflow.com/questions/3632918/…
Jaa H

9

Vecchio argomento, ma che ne dici di:

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

Mi piace di più come requisito in cui analizzare null, la versione TryParse non genererebbe un errore, ad esempio ToNullableInt32 (XXX). Ciò potrebbe introdurre errori silenziosi indesiderati.


1
Questo è esattamente il punto: se non è possibile analizzare la stringa int , dovrebbe tornare null, non generare un'eccezione.
svick,

1
se il valore non è numerico, int.Parse genera un'eccezione, che non equivale a restituire null.
un phu

8

Prova questo:

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

5

Sento che la mia soluzione è molto pulita e piacevole:

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

Questa è ovviamente una soluzione generica che richiede solo che l'argomento generics abbia un metodo statico "Parse (string)". Funziona con numeri, valori booleani, DateTime, ecc.


5

Puoi dimenticare tutte le altre risposte: esiste un'ottima soluzione generica: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Questo ti permette di scrivere un codice molto pulito come questo:

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

e anche:

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
Questo è davvero molto utile. Secondo me questo dovrebbe essere nelle librerie c # standard perché le conversioni sono molto comuni in ogni programma;)
BigChief

Questo è molto carino e utile, MA posso aggiungere che è estremamente lento quando è necessario effettuare conversioni per ogni elemento in una grande raccolta di articoli. Ho testato con 20000 articoli: utilizzando questo approccio, la conversione di 8 proprietà di ciascun articolo richiede fino a 1 ora per completare l'intera raccolta. Con gli stessi dati di esempio ma utilizzando l'approccio di Matt Hamilton, bastano pochi secondi per terminare.
zed

3

Quanto segue dovrebbe funzionare per qualsiasi tipo di struttura. È basato sul codice di Matt Manela dai forum MSDN . Come Murph sottolinea, la gestione delle eccezioni potrebbe essere costosa rispetto all'utilizzo del metodo TryParse dedicato ai tipi.

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

Questi erano i casi di test di base che ho usato.

        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

Suggerirei i seguenti metodi di estensione per l'analisi delle stringhe nel valore int con possibilità di definire il valore predefinito nel caso in cui l'analisi non sia possibile:

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

Ci sono già così tante e persino alte risposte votate. Pensi davvero che la tua risposta sia necessaria e aggiunga nuova qualità a questo post?
L. Guthardt,

1
@ L.Guthardt Sì, penso di sì. Dal momento che penso che la mia risposta porti un modo più universale per risolvere il problema descritto in questione. Grazie.
Aleksandr Neizvestnyi,

2

Questa soluzione è generica senza spese generali di riflessione.

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

Penso che puoi sostituire IsNullOrEmptyconIsNullOrWhitespace
NibblyPig il


1

Ho sentito che avrei dovuto condividere il mio, che è un po 'più generico.

Uso:

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

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

Soluzione:

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

La prima versione è più lenta poiché richiede un try-catch ma sembra più pulita. Se non verrà chiamato più volte con stringhe non valide, non è così importante. Se le prestazioni rappresentano un problema, tenere presente che quando si utilizzano i metodi TryParse, è necessario specificare il parametro type di ParseBy in quanto non può essere dedotto dal compilatore. Ho anche dovuto definire un delegato come parola chiave out non può essere utilizzata in Func <>, ma almeno questa volta il compilatore non richiede un'istanza esplicita.

Infine, puoi usarlo anche con altre strutture, ovvero decimale, DateTime, Guid, ecc.


1

Ho trovato e adattato del codice per una classe NullableParser generica. Il codice completo è sul mio 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;
        }
    }
}

1
404 non trovato. non è una buona pratica solo dare un link
Dirty-flow

scusate per l'aggiornamento @ flusso sporco con codice completo. Meglio tardi che mai :)
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
se myString non è un numero, int.Parse genera un'eccezione, che non equivale a restituire null.
un phu

0

Non dovresti mai usare un'eccezione se non devi - l'overhead è orribile.

Le variazioni su TryParse risolvono il problema - se vuoi essere creativo (per rendere il tuo codice più elegante) potresti probabilmente fare qualcosa con un metodo di estensione in 3.5 ma il codice sarebbe più o meno lo stesso.


0

Utilizzando i delegati, il codice seguente è in grado di fornire riusabilità se si ritiene che abbia bisogno dell'analisi nullable per più di un tipo di struttura. Ho mostrato qui entrambe le versioni .Parse () e. TryParse ().

Questo è un esempio di utilizzo:

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

Ed ecco il codice che ti porta lì ...

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

Ho trovato questo, che ha soddisfatto i miei requisiti (volevo che il mio metodo di estensione emulasse il più vicino possibile il ritorno del framework TryParse, ma senza provare i blocchi {} catch {} e senza che il compilatore si lamentasse di inferire un tipo nullable all'interno del metodo framework)

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

Suggerisco il codice qui sotto. È possibile lavorare con un'eccezione quando si è verificato un errore di conversione.

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

Utilizzare questo metodo di estensione nel codice (compilare la proprietà int? Age di una classe persona):

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

O

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.