Comment analyser une chaîne dans un entier nullable

300

Je veux analyser une chaîne dans un entier nullable en C #. c'est à dire. Je veux récupérer la valeur int de la chaîne ou null si elle ne peut pas être analysée.

J'espérais que ça marcherait

int? val = stringVal as int?;

Mais cela ne fonctionnera pas, donc la façon dont je le fais maintenant est que j'ai écrit cette méthode d'extension

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

Existe-t-il une meilleure façon de le faire?

EDIT: Merci pour les suggestions TryParse, je le savais, mais cela a fonctionné de la même manière. Je suis plus intéressé à savoir s'il existe une méthode de cadre intégrée qui analysera directement dans un int nullable?

Glenn Slaven
la source
1
Vous pouvez utiliser string.IsNullOrEmpty (valeur) pour obtenir la ligne if plus claire.
Özgür Kaplan
Envisagez d'utiliser la conversion générique stackoverflow.com/questions/773078/…
Michael Freidgeim

Réponses:

352

int.TryParse est probablement un peu plus facile:

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

Edit @Glenn int.TryParseest "intégré au framework". C'est et int.Parsesont le moyen d'analyser les chaînes en pouces.

Matt Hamilton
la source
82
une ligne de moins: renvoyer Int32.TryParse (s, out i)? i: null;
Chris Shouts
2
"a" renverra null, mais il n'est pas int et devrait lever l'exception
Arsen Mkrtchyan
54
@Chris, le compilateur n'aime pas votre instruction inline if (Ces types ne sont pas compatibles: 'int': 'null'). J'ai dû le modifier pour: retourner Int32.TryParse (s, out i)? (int?) i: null;
death_au
8
Int32 n'est qu'un alias de int. J'utiliserais int.TryParse pour garder les types utilisés dans l'alignement. Si / lorsque int est utilisé pour représenter un entier de longueur de bit différent (ce qui s'est produit), Int32 ne s'alignera pas avec int.
Richard Collette
4
return int.TryParse (s, out i)? (int?) i: null;
Nick Spreitzer
178

Vous pouvez le faire sur une seule ligne, en utilisant l'opérateur conditionnel et le fait que vous pouvez convertir nullen un type nullable (deux lignes, si vous n'avez pas un int préexistant, vous pouvez réutiliser pour la sortie de TryParse):

Pré C # 7:

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

Avec la syntaxe mise à jour de C # 7 qui vous permet de déclarer une variable de sortie dans l'appel de méthode, cela devient encore plus simple.

int? val = Int32.TryParse(stringVal, out var tempVal) ? tempVal : (int?)null;
McKenzieG1
la source
4
Cela dépend de votre vision de l'opérateur conditionnel, je pense. Mon modèle mental est que c'est à peu près du sucre syntaxique pour l'équivalent if-else, auquel cas ma version et celle de Matt sont presque identiques, sa version étant plus explicite, la mienne plus cmopact.
McKenzieG1
11
Il n'y a aucun effet secondaire d'ordre d'évaluation ici. Toutes les étapes sont explicitement ordonnées et correctes.
Jon Hanna
22
retourint.TryParse(val, out i) ? i : default(int?);
Bart Calixto
7
La "réponse" de Bart est la meilleure ici!
Andre Figueiredo
4
Et maintenant dans le C # 6, ça peut être une ligne! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA
34

[ Mis à jour pour utiliser le C # moderne selon la suggestion de @ sblom]

J'ai eu ce problème et je me suis retrouvé avec ça (après tout, un ifet 2 returns est tellement long!):

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

Sur une note plus sérieuse, essayez de ne pas mélanger int, qui est un mot clé C #, avec Int32, qui est un type BCL .NET Framework - bien que cela fonctionne, cela rend le code désordonné.

Duckboy
la source
3
Pas tout à fait sûr que cela se traduira réellement par quelque chose qui fonctionne mieux une fois compilé
BuZz
1
Encore plus concis en C # 7: supprimez la int i;ligne et allez-yreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom
2
Donc pour être complet ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy
Avec C # 6, cela peut être réduit à 1 ligne: return int.TryParse (value, out var result)? résultat: (int?) null;
MeanGreen
16

Glenn Slaven : Je suis plus intéressé à savoir s'il existe une méthode de framework intégrée qui analysera directement dans un int nullable?

Il y a cette approche qui analysera directement un int nullable (et pas seulement int) si la valeur est valide comme une chaîne nulle ou vide, mais lève une exception pour les valeurs non valides, vous devrez donc intercepter l'exception et renvoyer la valeur par défaut pour ces situations:

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

Cette approche peut toujours être utilisée pour les analyses non nullables ainsi que 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: Il existe une méthode IsValid sur le convertisseur que vous pouvez utiliser au lieu de capturer l'exception (les exceptions levées entraînent un surcoût inutile si prévu). Malheureusement, cela ne fonctionne que depuis .NET 4, mais il y a toujours un problème où il ne vérifie pas vos paramètres régionaux lors de la validation des formats DateTime corrects, voir le bogue 93559 .

Michael
la source
J'ai testé cela pour les nombres entiers et c'est beaucoup plus lent que int.TryParse (valeur (chaîne), résultat var var)? résultat: défaut (int?);
Wouter
12
var result = int.TryParse(foo, out var f) ? f : default(int?);

Sources:

Jaa H
la source
comment cela pourrait-il fonctionner? Tryparse ne fonctionnera pas ou les variables nullables et f dans votre exemple devraient être nullables.
John Lord
Pouvez-vous clarifier ce que vous voulez dire @JohnLord
Jaa H
tryparse s'attend à être placé dans une variable non nullable, donc votre valeur par défaut (int?) ne forcerait-elle pas null à être nullable?
John Lord
@JohnLord peut-être que cela vous aidera à comprendre ce qui se passe sur stackoverflow.com/questions/3632918/…
Jaa H
9

Ancien sujet, mais que diriez-vous:

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

J'aime mieux cela car le requriement où analyser null, la version TryParse ne jetterait pas d'erreur par exemple ToNullableInt32 (XXX). Cela peut introduire des erreurs silencieuses indésirables.

mortb
la source
1
C'est exactement le point - si la chaîne ne peut pas être analysée int, elle doit retourner null, ne pas lever d'exception.
svick
1
si la valeur n'est pas numérique, int.Parse lève une exception, ce qui n'est pas la même chose que retourner null.
un phu
8

Essaye ça:

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

Je pense que ma solution est une solution très propre et agréable:

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

Il s'agit bien sûr d'une solution générique qui nécessite uniquement que l'argument générique ait une méthode statique "Parse (string)". Cela fonctionne pour les nombres, booléens, DateTime, etc.

Lyskespark
la source
5

Vous pouvez oublier toutes les autres réponses - il existe une excellente solution générique: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Cela vous permet d'écrire du code très propre comme ceci:

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

et aussi:

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
la source
1
Ceci est en effet très utile. À mon avis, cela devrait être dans les bibliothèques c # standard car les conversions sont très courantes dans tous les programmes;)
BigChief
C'est très agréable et utile, MAIS je peux ajouter que c'est extrêmement lent quand il faut faire des conversions pour chaque élément d'une grande collection d'articles. J'ai testé avec 20000 articles: en utilisant cette approche, la conversion de 8 propriétés de chaque article prend jusqu'à 1 heure pour terminer la collection entière. Avec les mêmes données d'échantillonnage mais en utilisant l'approche de Matt Hamilton, cela ne prend que quelques secondes pour terminer.
zed
3

Les éléments suivants devraient fonctionner pour tout type de structure. Il est basé sur le code de Matt Manela des forums MSDN . Comme le souligne Murph, la gestion des exceptions pourrait être coûteuse par rapport à l'utilisation de la méthode TryParse dédiée aux types.

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

Ce sont les cas de test de base que j'ai utilisés.

        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
la source
3

Je suggérerais les méthodes d'extension suivantes pour l'analyse de chaîne en valeur int avec la possibilité de définir la valeur par défaut dans le cas où l'analyse n'est pas possible:

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
la source
Il y a déjà tant de réponses, et même des votes positifs. Pensez-vous vraiment que votre réponse est nécessaire et ajoute une nouvelle qualité à cet article?
L. Guthardt
1
@ L.Guthardt Oui, je pense que oui. Comme je pense que ma réponse apporte un moyen plus universel de résoudre le problème décrit en question. Je vous remercie.
Aleksandr Neizvestnyi
2

Cette solution est générique sans frais généraux de réflexion.

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
la source
Je pense que vous pouvez remplacer IsNullOrEmptyparIsNullOrWhitespace
NibblyPig
1

Je suis plus intéressé à savoir s'il existe une méthode de cadre intégrée qui analysera directement dans un int nullable?

Il n'y en a pas.

Orion Edwards
la source
1
Considérez-vous cela comme une approche directe? stackoverflow.com/a/6474962/222748
Michael
1

J'ai senti que je devrais partager le mien qui est un peu plus générique.

Usage:

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

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

Solution:

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 première version est plus lente car elle nécessite un essai mais elle semble plus propre. Si elle ne sera pas appelée plusieurs fois avec des chaînes invalides, ce n'est pas si important. Si les performances sont un problème, veuillez noter que lorsque vous utilisez des méthodes TryParse, vous devez spécifier le paramètre type de ParseBy car il ne peut pas être déduit par le compilateur. J'ai également dû définir un délégué car le mot clé out ne peut pas être utilisé dans Func <>, mais au moins cette fois, le compilateur ne nécessite pas d'instance explicite.

Enfin, vous pouvez également l'utiliser avec d'autres structures, à savoir décimal, DateTime, Guid, etc.

orcun
la source
1

J'ai trouvé et adapté du code pour une classe Generic NullableParser. Le code complet est sur mon 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
la source
1
404 - introuvable. ce n'est pas une bonne pratique juste de donner un lien
Dirty-flow
désolé pour cette mise à jour @ Dirty-flow avec le code complet. Mieux vaut tard que jamais :)
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
la source
1
si myString n'est pas numérique, int.Parse lève une exception, ce qui n'est pas la même chose que retourner null.
un phu
0

Tu ne devrais jamais utiliser d'exception si vous n'êtes pas obligé - les frais généraux sont horribles.

Les variations de TryParse résolvent le problème - si vous voulez faire preuve de créativité (pour rendre votre code plus élégant), vous pourriez probablement faire quelque chose avec une méthode d'extension en 3.5 mais le code serait plus ou moins le même.

Murph
la source
0

À l'aide de délégués, le code suivant est en mesure de fournir une réutilisabilité si vous avez besoin de l'analyse syntaxique nullable pour plusieurs types de structure. J'ai montré les versions .Parse () et .TryParse () ici.

Ceci est un exemple d'utilisation:

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

Et voici le code qui vous y mène ...

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
la source
0

Je me rends compte que c'est un vieux sujet, mais ne pouvez-vous pas simplement:

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

?

Leigh Bowers
la source
Vous pouvez, mais vous obtiendrez alors une exception si stringVal est au mauvais format. Voir la documentation int.Parse: msdn.microsoft.com/en-us/library/b3h1hf19.aspx
Alex
0

J'ai trouvé celui-ci, qui a satisfait mes exigences (je voulais que ma méthode d'extension émule le plus près possible le retour du framework TryParse, mais sans essayer les blocs {} catch {} et sans que le compilateur se plaint de déduire un type nullable dans la méthode du 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;
}
wmoecke
la source
0

Je suggère le code ci-dessous. Vous pouvez travailler avec exception, lorsqu'une erreur de conversion s'est produite.

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

Utilisez cette méthode d'extension dans le code (fill int? Propriété d'âge d'une classe de personne):

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

OU

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