Como analisar uma string em um int nulo


300

Eu estou querendo analisar uma seqüência de caracteres em um int anulável em c #. ie Eu quero voltar o valor int da string ou null se não puder ser analisado.

Eu meio que esperava que isso funcionasse

int? val = stringVal as int?;

Mas isso não vai funcionar, então o jeito que eu estou fazendo isso agora é que eu escrevi esse método de extensão

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 um jeito melhor de fazer isso?

Edição: Obrigado pelas sugestões TryParse, eu sabia sobre isso, mas funcionou sobre o mesmo. Estou mais interessado em saber se existe um método de estrutura interno que será analisado diretamente em um int nulo?


1
Você pode usar string.IsNullOrEmpty (value) para obter a linha if mais clara.
Özgür Kaplan

Considere usar a conversão de genéricos stackoverflow.com/questions/773078/…
Michael Freidgeim 15/06/16

Respostas:


352

int.TryParse é provavelmente um pouco mais fácil:

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

Editar @Glenn int.TryParseé "incorporado à estrutura". É e int.Parseé o caminho para analisar cadeias de caracteres para ints.


82
uma linha a menos: retornar Int32.TryParse (s, fora i)? i: nulo;
Chris Shouts

2
"a" retornará null, mas não é int e deve lançar exceção
Arsen Mkrtchyan

54
@ Chris, o compilador não gosta da instrução if inline (esses tipos não são compatíveis: 'int': 'null'). Eu tive que corrigi-lo para: retornar Int32.TryParse (s, fora i)? (int?) i: nulo;
death_au

8
Int32 é apenas um alias para int. Eu usaria int.TryParse para manter os tipos sendo usados ​​em alinhamento. Se / quando int for usado para representar um número inteiro diferente de comprimento de bit (o que aconteceu), Int32 não se alinhará com int.
precisa

4
return int.TryParse (s, fora i)? (int?) i: nulo;
Nick Spreitzer

178

Você pode fazer isso em uma linha, usando o operador condicional e o fato de poder converter nullpara um tipo nulo (duas linhas, se você não tiver um int pré-existente, poderá reutilizar para a saída de TryParse):

Pré C # 7:

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

Com a sintaxe atualizada do C # 7 que permite declarar uma variável de saída na chamada de método, isso fica ainda mais simples.

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

4
Depende da sua visão do operador condicional, eu acho. Meu modelo mental é que é praticamente um açúcar sintático para o equivalente if-else, caso em que minha versão e a de Matt são quase idênticas, sendo ele mais explícito, mais meu cmopacto.
McKenzieG1

11
Não há efeito colateral da ordem de avaliação aqui. Todas as etapas estão explicitamente ordenadas e corretas.
Jon Hanna

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

7
A "resposta" de Bart é a melhor aqui!
Andre Figueiredo

4
E agora no C # 6, pode ser uma linha! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) nulo;
MerickOWA

34

[ Atualizado para usar o C # moderno conforme a sugestão do @ sblom]

Eu tive esse problema e acabei com isso (afinal, an ife 2 returns são muito longos!):

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

Em uma observação mais séria, tente não misturar int, que é uma palavra-chave C #, com Int32, que é do tipo .NET Framework BCL - embora funcione, apenas torna o código confuso.


3
Não é bem certo isso vai realmente traduzir em qualquer coisa que executa melhor, uma vez compilado
zumbido

1
Ainda mais conciso em C # 7: excluir a int i;linha e apenas ir comreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
Então, para ser completo ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

Com o C # 6, isso pode ser reduzido para 1 linha: return int.TryParse (value, out var result)? resultado: (int?) nulo;
precisa saber é o seguinte

16

Glenn Slaven : Estou mais interessado em saber se existe um método de estrutura interno que será analisado diretamente em um int nulo?

Existe essa abordagem que analisará diretamente um int nulo (e não apenas int) se o valor for válido como uma seqüência nula ou vazia, mas lança uma exceção para valores inválidos, portanto você precisará capturar a exceção e retornar o valor padrão para essas situações:

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

Essa abordagem ainda pode ser usada para análises não anuláveis ​​e anuláveis:

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

Nota: existe um método IsValid no conversor que você pode usar em vez de capturar a exceção (exceções lançadas resultam em sobrecarga desnecessária, se esperado). Infelizmente, ele só funciona desde o .NET 4, mas ainda existe um problema em que ele não verifica seu código do idioma ao validar os formatos corretos de DateTime, veja o bug 93559 .


Eu testei isso para números inteiros e é muito mais lento que o int.TryParse ((string) value, out var result)? resultado: padrão (int?);
Wouter

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

Fontes:


como isso funcionou? Tryparse não funcionará ou variáveis ​​anuláveis ​​ef no seu exemplo teria que ser anulável.
John Lord

Por favor, você pode esclarecer o que você quer dizer @JohnLord
Jaa H

O tryparse espera ser colocado em uma variável não anulável. Portanto, seu padrão (int?) força a var a ser anulável?
John Lord

@JohnLord talvez isso ajude você a entender o que está acontecendo no stackoverflow.com/questions/3632918/…
Jaa H

9

Tópico antigo, mas que tal:

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

Eu gosto mais disso como o requisito de analisar nulo, a versão TryParse não geraria um erro, por exemplo, em ToNullableInt32 (XXX). Isso pode introduzir erros silenciosos indesejados.


1
Esse é exatamente o ponto - se a string não puder ser analisada int, ela deve retornar null, não lançar uma exceção.
svick

1
se o valor for não numérico, int.Parse lança uma exceção, que não é o mesmo que retornar nulo.
An Phu

8

Tente o seguinte:

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

5

Sinto que minha solução é uma solução muito limpa e agradável:

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

Obviamente, essa é uma solução genérica que exige apenas que o argumento genérico tenha um método estático "Parse (string)". Isso funciona para números, booleano, DateTime etc.


5

Você pode esquecer todas as outras respostas - existe uma ótima solução genérica: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Isso permite que você escreva um código muito limpo como este:

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

e também:

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
Isso é realmente muito útil. Na minha opinião isso deve estar nas bibliotecas padrão c # porque as conversões são muito comuns em cada programa;)
BigChief

Isso é muito agradável e útil, mas devo acrescentar que é extremamente lento quando é necessário fazer conversões para cada elemento em uma grande coleção de itens. Testei com 20000 itens: usando essa abordagem, a conversão de 8 propriedades de cada item leva até 1 hora para concluir toda a coleção. Com os mesmos dados de amostra, mas usando a abordagem de Matt Hamilton, leva apenas alguns segundos para concluir.
zed

3

O seguinte deve funcionar para qualquer tipo de estrutura. É baseado no código de Matt Manela nos fóruns do MSDN . Como Murph aponta, o tratamento de exceções pode ser caro comparado ao uso do método TryParse de Tipos dedicado.

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

Esses foram os casos de teste básicos que eu usei.

        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

Eu sugeriria os seguintes métodos de extensão para análise de string no valor int com capacidade de definir o valor padrão, caso a análise não seja possível:

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

Já existem muitas e até altas respostas votadas. Você realmente acha que sua resposta é necessária e acrescenta nova qualidade a este post?
L. Guthardt 12/03/19

1
@ L.Guthardt Sim, acho que sim. Como acho que minha resposta traz uma maneira mais universal de resolver o problema descrito em questão. Obrigado.
Aleksandr Neizvestnyi 12/03/19

2

Esta solução é genérica sem sobrecarga de reflexão.

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

Eu acho que você pode substituir IsNullOrEmptycomIsNullOrWhitespace
NibblyPig


1

Eu senti que deveria compartilhar o meu, que é um pouco mais genérico.

Uso:

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

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

Solução:

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

A primeira versão é mais lenta, pois requer uma tentativa de captura, mas parece mais limpa. Se não for chamado muitas vezes com cadeias inválidas, isso não é importante. Se o desempenho for um problema, observe que, ao usar os métodos TryParse, você precisa especificar o parâmetro type de ParseBy, pois não pode ser inferido pelo compilador. Também tive que definir um delegado como a palavra-chave out não pode ser usada no Func <>, mas pelo menos desta vez o compilador não requer uma instância explícita.

Finalmente, você pode usá-lo com outras estruturas, como decimal, DateTime, Guid, etc.


1

Encontrei e adaptei algum código para uma classe NullableParser genérica. O código completo está no meu 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 não encontrado. não é uma boa prática apenas fornecer um link
Dirty-flow

desculpe por essa atualização de fluxo sujo com código completo. Melhor tarde do que nunca :)
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 não for numérico, int.Parse lança uma exceção, que não é a mesma coisa que retornar nulo.
An Phu

0

Você nunca deve usar uma exceção se não precisar - a sobrecarga é horrível.

As variações no TryParse resolvem o problema - se você quiser ser criativo (para tornar seu código mais elegante), provavelmente poderá fazer algo com um método de extensão na versão 3.5, mas o código será mais ou menos o mesmo.


0

Usando delegados, o código a seguir poderá fornecer reutilização se você precisar da análise anulável para mais de um tipo de estrutura. Eu mostrei as versões .Parse () e .TryParse () aqui.

Este é um exemplo de uso:

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

E aqui está o código que leva você até 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

Eu criei este, que atendeu aos meus requisitos (eu queria que meu método de extensão emulasse o mais próximo possível do retorno do TryParse da estrutura, mas sem os blocos try {} catch {} e sem o compilador reclamar tipo anulável dentro do método de estrutura)

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

Eu sugiro o código abaixo. Você pode trabalhar com exceção, quando ocorreu um erro de conversão.

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

Use este método de extensão no código (fill int? Age propriedade de uma classe de pessoa):

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); });
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.