Cómo analizar una cadena en un int anulable


300

Quiero analizar una cadena en un int anulable en C #. es decir. Quiero recuperar el valor int de la cadena o nulo si no se puede analizar.

Tenía la esperanza de que esto funcionara

int? val = stringVal as int?;

Pero eso no funcionará, así que la forma en que lo hago ahora es que he escrito este método de extensión

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

¿Hay una mejor manera de hacer esto?

EDITAR: Gracias por las sugerencias de TryParse, lo sabía, pero funcionó de la misma manera. ¿Estoy más interesado en saber si hay un método de marco incorporado que se analizará directamente en un int con nulos?


1
Puede usar string.IsNullOrEmpty (value) para obtener la línea if más clara.
Özgür Kaplan

Considere utilizar la conversión de genéricos stackoverflow.com/questions/773078/…
Michael Freidgeim

Respuestas:


352

int.TryParse es probablemente un poco más fácil:

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

Editar @Glenn int.TryParseestá "integrado en el marco". Es y int.Parsees la forma de analizar cadenas a ints.


82
una línea menos: return Int32. TryParse (s, out i)? i: nulo;
Chris grita el

2
"a" devolverá nulo, pero no es int y debería arrojar una excepción
Arsen Mkrtchyan

54
@Chris, al compilador no le gusta su instrucción if en línea (estos tipos no son compatibles: 'int': 'nulo'). Tuve que modificarlo para: return Int32. TryParse (s, out i)? (int?) i: nulo;
death_au

8
Int32 es solo un alias para int. Usaría int. TryParse para mantener los tipos que se usan en alineación. Si / cuando int se usa para representar un entero de longitud de bit diferente (lo que ha sucedido), Int32 no se alineará con int.
Richard Collette

44
volver int. TryParse (s, fuera i)? (int?) i: nulo;
Nick Spreitzer

178

Puede hacer esto en una línea, utilizando el operador condicional y el hecho de que puede convertir nulla un tipo anulable (dos líneas, si no tiene un int preexistente puede reutilizar para la salida de TryParse):

Pre C # 7:

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

Con la sintaxis actualizada de C # 7 que le permite declarar una variable de salida en la llamada al método, esto se vuelve aún más simple.

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

44
Eso depende de su punto de vista del operador condicional, creo. Mi modelo mental es que es más o menos el azúcar sintáctico para el equivalente si no, en cuyo caso mi versión y la de Matt son casi idénticas, siendo más explícito, el mío más cmopact.
McKenzieG1

11
No hay ningún efecto secundario de orden de evaluación aquí. Todos los pasos están explícitamente ordenados y son correctos.
Jon Hanna

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

77
¡La "respuesta" de @ Bart es la mejor aquí!
Andre Figueiredo

44
¡Y ahora en el C # 6, puede ser una línea! Int32. TryParse (stringVal, out var tempVal)? tempVal: (int?) nulo;
MerickOWA

34

[ Actualizado para usar C # moderno según la sugerencia de @ sblom]

Tuve este problema y terminé con esto (después de todo, ¡an ify 2 returns son muy largos!):

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

En una nota más seria, intente no mezclar int, que es una palabra clave de C #, con Int32, que es un tipo .CL Framework BCL, aunque funciona, solo hace que el código se vea desordenado.


3
No estoy seguro de que esto realmente se traduzca en algo que
funcione

1
Aún más conciso en C # 7: elimine la int i;línea y simplemente continúe conreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
Así que para completar ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

Con C # 6, esto se puede reducir a 1 línea: ¿devolver int. TryParse (valor, resultado var)? resultado: (int?) nulo;
MeanGreen

16

Glenn Slaven : ¿Estoy más interesado en saber si hay un método de marco incorporado que se analizará directamente en un int con anulación?

Existe este enfoque que se analizará directamente a un int anulable (y no solo int) si el valor es válido como una cadena nula o vacía, pero arroja una excepción para valores no válidos, por lo que deberá detectar la excepción y devolver el valor predeterminado para esas situaciones:

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

Este enfoque todavía se puede usar para análisis no anulables y anulables:

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: hay un método IsValid en el convertidor que puede usar en lugar de capturar la excepción (las excepciones generadas generan una sobrecarga innecesaria si se espera). Desafortunadamente, solo funciona desde .NET 4, pero todavía hay un problema en el que no comprueba su configuración regional al validar los formatos correctos de DateTime, vea el error 93559 .


Probé esto para enteros y es mucho más lento que int. TryParse (valor (cadena), resultado var) resultado: predeterminado (int?);
Wouter

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

Fuentes:


¿Cómo podría funcionar esto? Tryparse no funcionará o las variables anulables y f en su ejemplo tendrían que ser anulables.
John Lord

¿Puedes aclarar lo que quieres decir @JohnLord
Jaa H

tryparse espera colocarse en una variable no anulable, por lo que su valor predeterminado (int?) no obligaría a var a ser anulable?
John Lord

@JohnLord tal vez esto te ayude a comprender lo que está sucediendo en stackoverflow.com/questions/3632918/…
Jaa H

9

Viejo tema, pero ¿qué tal:

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

Me gusta más como el requisito de analizar nulo, la versión TryParse no arrojaría un error, por ejemplo, ToNullableInt32 (XXX). Eso puede introducir errores silenciosos no deseados.


1
Ese es exactamente el punto: si la cadena no se puede analizar int, debe volver null, no lanzar una excepción.
svick

1
si el valor no es numérico, int.Parse produce una excepción, que no es lo mismo que devolver nulo.
un phu

8

Prueba esto:

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

5

Siento que mi solución es una solución muy limpia y agradable:

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

Por supuesto, esta es una solución genérica que solo requiere que el argumento genérico tenga un método estático "Parse (string)". Esto funciona para números, booleanos, DateTime, etc.


5

Puede olvidarse de todas las demás respuestas: hay una gran solución genérica: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Esto le permite escribir código muy limpio como este:

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

y también:

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
Esto es de hecho muy útil. En mi opinión, esto debería estar en las bibliotecas estándar de C # porque las conversiones son muy comunes en todos los programas;)
BigChief

Esto es muy agradable y útil, PERO puedo agregar que es extremadamente lento cuando es necesario realizar conversiones para cada elemento en una gran colección de elementos. He probado con 20000 artículos: al usar este enfoque, la conversión de 8 propiedades de cada artículo lleva hasta 1 hora para terminar la colección completa. Con los mismos datos de muestra pero usando el enfoque de Matt Hamilton, solo toma unos segundos terminar.
zed

3

Lo siguiente debería funcionar para cualquier tipo de estructura. Está basado en el código de Matt Manela de los foros de MSDN . Como señala Murph, el manejo de excepciones podría ser costoso en comparación con el uso del método TryParse dedicado de Tipos.

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

Estos fueron los casos de prueba básicos que utilicé.

        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

Sugeriría seguir los métodos de extensión para el análisis de cadenas en valor int con la capacidad de definir el valor predeterminado en caso de que el análisis no sea posible:

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

Ya hay muchas e incluso respuestas muy votadas. ¿Realmente crees que tu respuesta es necesaria y agrega nueva calidad a esta publicación?
L. Guthardt el

1
@ L.Guthardt Sí, creo que sí. Como creo que mi respuesta trae una forma más universal de resolver el problema que se describe en cuestión. Gracias.
Aleksandr Neizvestnyi

2

Esta solución es genérica sin reflexión sobrecarga.

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

Creo que puede reemplazar IsNullOrEmptyconIsNullOrWhitespace
NibblyPig


1

Sentí que debería compartir el mío, que es un poco más genérico.

Uso:

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

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

Solución:

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 primera versión es más lenta ya que requiere un try-catch pero parece más limpio. Si no se llamará muchas veces con cadenas no válidas, no es tan importante. Si el rendimiento es un problema, tenga en cuenta que cuando utiliza los métodos TryParse, debe especificar el parámetro de tipo de ParseBy, ya que el compilador no puede inferirlo. También tuve que definir un delegado ya que la palabra clave out no se puede usar dentro de Func <>, pero al menos esta vez el compilador no requiere una instancia explícita.

Finalmente, puede usarlo también con otras estructuras, es decir, decimal, DateTime, Guid, etc.


1

Encontré y adapté un código para una clase Generic NullableParser. El código completo está en mi 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 No encontrado. no es una buena práctica solo dar un enlace
flujo sucio

lo siento por esa actualización de flujo sucio con código completo. Más vale tarde 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
si myString no es numérico, int.Parse genera una excepción, que no es lo mismo que devolver nulo.
un phu

0

Usted debe nunca se utilice una excepción si no tiene que - la sobrecarga es horrible.

Las variaciones en TryParse resuelven el problema: si desea ser creativo (para que su código se vea más elegante), probablemente podría hacer algo con un método de extensión en 3.5, pero el código sería más o menos el mismo.


0

Usando delegados, el siguiente código puede proporcionar la reutilización si necesita el análisis anulable para más de un tipo de estructura. He mostrado las versiones .Parse () y. TryParse () aquí.

Este es un ejemplo de uso:

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

Y aquí está el código que te lleva allí ...

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

Se me ocurrió este, que ha satisfecho mis requisitos (quería que mi método de extensión emulara lo más cerca posible el retorno del TryParse del marco, pero sin los bloques try {} catch {} y sin el compilador quejándose de inferir un tipo anulable dentro del método 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

Sugiero el siguiente código. Puede trabajar con excepción, cuando ocurre el error de conversión.

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

Utilice este método de extensión en el código (rellenar int? Propiedad de edad de una clase de 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); });
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.