Pergunta

Estou querendo analisar uma string em um int anulável em C#.ou seja.Quero recuperar 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 estou fazendo isso agora, escrevendo este 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?

EDITAR: Obrigado pelas sugestões do TryParse, eu sabia disso, mas funcionou da mesma forma.Estou mais interessado em saber se existe um método de estrutura integrado que será analisado diretamente em um int anulável.

Foi útil?

Solução

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 está "incorporado à estrutura".Isso e int.Parse são o maneira de analisar strings em ints.

Outras dicas

Você pode fazer isso em uma linha, usando o operador condicional e o fato de poder converter null para um tipo anulável (duas linhas, se você não tiver um int pré-existente, poderá reutilizá-lo 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 do método, isso fica ainda mais simples.

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

Eu tive esse problema e acabei com isso (afinal, um if e 2 returns é tão prolixo!):

int? ParseNInt (string val)
{
    int i;
    return int.TryParse (val, out i) ? (int?) i : null;
}

Falando mais sério, tente não misturar int, que é uma palavra-chave C#, com Int32, que é um tipo BCL do .NET Framework - embora funcione, apenas faz o código parecer confuso.

Glenn Slaven:Estou mais interessado em saber se existe um método de estrutura integrado que analisará diretamente um Int Nullable?

Existe esta abordagem que irá analisar diretamente para um int anulável (e não apenas int) se o valor for válido como uma string nula ou vazia, mas lança uma exceção para valores inválidos, então 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, bem como 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%");

Observação: 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 há um problema em que ele não verifica sua localidade ao validar os formatos DateTime corretos, consulte erro 93559.

Experimente isto:

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

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 onde analisar nulo, a versão TryParse não geraria um erro, por exemplo.ToNullableInt32(XXX).Isso pode introduzir erros silenciosos indesejados.

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

É claro que esta é uma solução genérica que requer apenas que o argumento genérico tenha um método estático "Parse (string)".Isso funciona para números, booleanos, DateTime, etc.

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

Fontes:

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

O seguinte deve funcionar para qualquer tipo de estrutura.É baseado no código de Matt Manela dos fóruns do MSDN.Como Murph aponta, o tratamento de exceções pode ser caro em comparação ao uso do método TryParse dedicado a 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;
        }

Esses foram os casos de teste básicos que 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);

Eu sugeriria seguir os métodos de extensão para análise de string em 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();
        }

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

Estou mais interessado em saber se existe um método de estrutura integrado que será analisado diretamente em um int anulável.

Não existe.

Achei 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 um try-catch, mas parece mais limpa.Se não for chamado muitas vezes com strings inválidas, não é tão importante.Se o desempenho for um problema, observe que ao usar métodos TryParse, você precisa especificar o parâmetro de tipo ParseBy, pois ele não pode ser inferido pelo compilador.Eu também tive que definir um delegado, pois a palavra-chave out não pode ser usada em Func<>, mas pelo menos desta vez o compilador não requer uma instância explícita.

Finalmente, você também pode usá-lo com outras estruturas, ou seja,decimal, DateTime, Guid, etc.

Encontrei e adaptei alguns códigos para uma classe NullableParser genérica.O código completo está no meu blog TryParse anulável

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

Você deve nunca use uma exceção se não for necessário - a sobrecarga é horrível.

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

Usando delegados, o código a seguir é capaz de fornecer capacidade de reutilização se você precisar da análise anulável para mais de um tipo de estrutura.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));
    }
  }

Eu criei este, que satisfez meus requisitos (eu queria que meu método de extensão emulasse o mais próximo possível o retorno do TryParse da estrutura, mas sem blocos try{} catch{} e sem que o compilador reclamasse de inferir um 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;
}

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 (preencha int?Propriedade de idade 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); });

Sei que este é um tópico antigo, mas você não pode simplesmente:

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

?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top