Как разобрать строку в числовое значение, допускающее значение NULL

StackOverflow https://stackoverflow.com/questions/45030

Вопрос

Я хочу проанализировать строку в числовое значение, допускающее значение NULL, в С#.то есть.Я хочу вернуть либо int-значение строки, либо значение null, если его невозможно проанализировать.

Я надеялся, что это сработает

int? val = stringVal as int?;

Но это не сработает, поэтому я сейчас делаю это так: я написал этот метод расширения

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

Есть ли лучший способ сделать это?

РЕДАКТИРОВАТЬ: Спасибо за предложения TryParse, я знал об этом, но получилось примерно то же самое.Мне больше интересно узнать, существует ли встроенный метод фреймворка, который будет анализировать непосредственно nullable int?

Это было полезно?

Решение

int.TryParse наверное, немного проще:

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

Редактировать @Гленн int.TryParse «встроен в структуру».Это и int.Parse являются тот способ синтаксического анализа строк в целые числа.

Другие советы

Вы можете сделать это в одной строке, используя условный оператор и тот факт, что вы можете привести null к типу, допускающему значение NULL (две строки, если у вас нет ранее существовавшего int, вы можете повторно использовать его для вывода TryParse):

До С#7:

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

Благодаря обновленному синтаксису C#7, который позволяет объявлять выходную переменную при вызове метода, это становится еще проще.

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

У меня была такая проблема, в итоге я получил вот это (в конце концов, if и 2 returnэто так многословно!):

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

А если серьезно, постарайтесь не смешивать int, которое является ключевым словом C#, с Int32, который является типом BCL .NET Framework — хотя он и работает, он просто делает код беспорядочным.

Гленн Славен:Мне больше интересно знать, есть ли встроенный метод фреймворта, который будет разобраться непосредственно в нулевом int?

Существует этот подход, который будет анализировать непосредственно числовое значение, допускающее значение NULL (а не только int), если значение допустимо, например, нулевое значение или пустую строку, но выдает исключение для недопустимых значений, поэтому вам нужно будет перехватить исключение и вернуть значение по умолчанию. для таких ситуаций:

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

Этот подход все еще можно использовать для синтаксического анализа, не допускающего значения NULL, а также для анализа, допускающего значение NULL:

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

Примечание: В конвертере есть метод IsValid, который вы можете использовать вместо перехвата исключения (вызываемые исключения приводят к ненужные накладные расходы если это ожидается).К сожалению, он работает только начиная с .NET 4, но все еще существует проблема, связанная с тем, что он не проверяет ваш языковой стандарт при проверке правильных форматов DateTime, см. ошибка 93559.

Попробуй это:

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

Старая тема, но как насчет:

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

Мне это больше нравится, так как требование, где анализировать значение null, версия TryParse не будет выдавать ошибку, например.ТоНуллаблеИнт32(ХХХ).Это может привести к нежелательным скрытым ошибкам.

Я считаю, что мое решение - очень чистое и красивое решение:

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

Это, конечно, общее решение, которое требует только, чтобы аргумент дженериков имел статический метод «Parse(string)».Это работает для чисел, логических значений, DateTime и т. д.

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

Источники:

Вы можете забыть все остальные ответы — есть отличное общее решение:http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Это позволяет вам писать очень чистый код, например:

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

а также:

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

Следующее должно работать для любого типа структуры.Он основан на коде Мэтт Манела с форумов MSDN.Как отмечает Мерф, обработка исключений может быть дорогостоящей по сравнению с использованием специального метода TryParse 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;
        }

Это были основные тестовые примеры, которые я использовал.

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

Я бы предложил следующие методы расширения для анализа строки в значение int с возможностью определения значения по умолчанию в случае, если анализ невозможен:

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

Это решение является общим, без накладных расходов на отражение.

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

Мне больше интересно узнать, существует ли встроенный метод фреймворка, который будет анализировать непосредственно nullable int?

Нет.

Я почувствовал, что должен поделиться своим, более общим.

Использование:

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

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

Решение:

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

Первая версия медленнее, так как требует попытки, но выглядит чище.Если он не будет вызываться много раз с недопустимыми строками, это не так важно.Если производительность является проблемой, обратите внимание, что при использовании методов TryParse вам необходимо указать параметр типа ParseBy, поскольку он не может быть выведен компилятором.Мне также пришлось определить делегата, поскольку ключевое слово out нельзя использовать в Func<>, но, по крайней мере, на этот раз компилятор не требует явного экземпляра.

Наконец, вы можете использовать его и с другими структурами, т.е.десятичное число, DateTime, Guid и т. д.

Я нашел и адаптировал код для класса Generic NullableParser.Полный код находится в моем блоге Обнуляемый 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;
        }
    }
}
    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;
    }

Вам следует никогда используйте исключение, если в этом нет необходимости — накладные расходы ужасны.

Вариации TryParse решают проблему: если вы хотите проявить творческий подход (чтобы ваш код выглядел более элегантно), вы, вероятно, могли бы сделать что-то с помощью метода расширения в версии 3.5, но код будет более или менее таким же.

Используя делегаты, следующий код может обеспечить возможность повторного использования, если вам понадобится синтаксический анализ, допускающий значение NULL, для более чем одного типа структуры.Здесь я показал обе версии .Parse() и .TryParse().

Это пример использования:

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

И вот код, который приведет вас туда...

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

Я придумал этот вариант, который удовлетворил мои требования (я хотел, чтобы мой метод расширения как можно ближе имитировал возврат TryParse фреймворка, но без блоков try{} catch{} и без жалоб компилятора на вывод обнуляемый тип в рамках метода рамки)

private static bool TryParseNullableInt(this string s, out int? result)
{
    int i;
    result = int.TryParse(s, out i) ? (int?)i : null;
    return result != null;
}

Я предлагаю код ниже.Вы можете работать с исключением, если произошла ошибка конвертации.

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

Используйте этот метод расширения в коде (заполните int?Свойство возраста класса человека):

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

ИЛИ

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

Я понимаю, что это старая тема, но не могли бы вы просто:

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

?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top