Как разобрать строку на обнуляемое int

Я хочу преобразовать строку в обнуляемое int в C #. т.е. Я хочу вернуть либо значение 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, я знал об этом, но это сработало примерно так же. Мне больше интересно узнать, есть ли встроенный метод фреймворка, который будет анализировать непосредственно в int, допускающий значение NULL?


person Glenn Slaven    schedule 05.09.2008    source источник
comment
Вы можете использовать string.IsNullOrEmpty (value), чтобы сделать строку if более четкой.   -  person Özgür Kaplan    schedule 29.02.2012
comment
Рассмотрите возможность использования преобразования универсальных типов stackoverflow.com/questions/773078/   -  person Michael Freidgeim    schedule 16.06.2016


Ответы (21)


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

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

Изменить @Glenn int.TryParse "встроен в структуру". Он и int.Parse - это способ преобразования строк в целые числа.

person Matt Hamilton    schedule 05.09.2008
comment
на одну строку меньше: return Int32.TryParse (s, out i)? я: ноль; - person Chris Shouts; 23.10.2009
comment
Я определенно предпочитаю этот способ TryParse - person Bent Rasmussen; 11.03.2010
comment
Я собирался опубликовать пример, указывающий на то, что возникнет исключение NullReferenceException, если строка будет нулевой. Вместо этого я узнал кое-что интересное о методах расширения. строка parseOne = 1; int? resultOne = parseOne.ToNullableInt32 (); System.Diagnostics.Debug.Assert (resultOne == 1); строка parseEmpty = строка.Empty; int? resultEmpty = parseEmpty.ToNullableInt32 (); System.Diagnostics.Debug.Assert (! ResultEmpty.HasValue); строка parseNull = null; int? resultNull = parseNull.ToNullableInt32 (); System.Diagnostics.Debug.Assert (! ResultNull.HasValue); - person Daniel Ballinger; 09.04.2010
comment
a вернет null, но это не int и должно вызывать исключение - person Arsen Mkrtchyan; 19.07.2011
comment
@Arsen ... именно это и задавал первоначальный вопрос. Посмотрите на его фрагмент кода. - person Matt Hamilton; 19.07.2011
comment
@Chris, компилятору не нравится ваш встроенный оператор if (эти типы несовместимы: 'int': 'null'). Мне пришлось изменить его на: return Int32.TryParse (s, out i)? (int?) я: ноль; - person death_au; 25.01.2012
comment
Int32 - это просто псевдоним int. Я бы использовал int.TryParse, чтобы сохранить выравнивание используемых типов. Если / когда int используется для представления целого числа другой длины в битах (что произошло), Int32 не будет совпадать с int. - person Richard Collette; 26.11.2012
comment
return int.TryParse (s, out i)? (int?) я: ноль; - person Nick Spreitzer; 15.07.2015
comment
общедоступный статический интервал? _ni (эта строка s) {int i; return int.TryParse (s, out i)? (int?) i: null;} - person Martin Capodici; 27.01.2016
comment
@death_au i: новый интервал? () - person ccalboni; 30.05.2016
comment
@ccalboni тоже работает. Как и i : default(int?). Подумав об этом, в ответ он все равно приведет результат к int?, так что, может быть, лучше, если я просто сделаю это? Не уверен. - person death_au; 01.06.2016
comment
Еще одна строка меньше в С # 7 (встроенное объявление i): int.TryParse (s, out int i)? (int?) я: ноль; - person Darren; 14.09.2016
comment
общедоступный статический интервал? ToNullableInt (эта строка s) = ›(int.TryParse (s, out int i))? я: (число?) ноль; - person IamIC; 20.06.2019
comment
Использование встроенного объявления и default с выводом типа int.TryParse(s, out var i) ? i as int? : default - person Morgan Touverey Quilling; 13.12.2019

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

До C # 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;
person McKenzieG1    schedule 05.09.2008
comment
Вы могли бы - но полагаться на побочные эффекты и подобный порядок оценки мерзко, когда вы можете сделать последовательную зависимость очевидной, используя синтаксис Мэтта. Между прочим, для этого и подобных выражений удобно использовать default (int?) Именно для того, чтобы избежать ошибок вывода типов, которые проистекают из нетипизированной природы null, без необходимости вставки приведений, которые могут фактически выполнять код. - person Eamon Nerbonne; 31.05.2010
comment
Я думаю, это зависит от вашего взгляда на условный оператор. Моя ментальная модель состоит в том, что это в значительной степени синтаксический сахар для эквивалента if-else, и в этом случае моя версия и версия Мэтта близки к идентичности, причем он более явный, моя более cmopact. - person McKenzieG1; 02.06.2010
comment
Здесь нет побочного эффекта порядка оценки. Все шаги четко упорядочены и правильны. - person Jon Hanna; 22.03.2011
comment
вернуть int.TryParse(val, out i) ? i : default(int?); - person Bart Calixto; 12.12.2013
comment
@ Ответ Барта здесь лучший! - person Andre Figueiredo; 20.12.2013
comment
А теперь в C # 6 это может быть одна строка! Int32.TryParse (stringVal, out var tempVal)? tempVal: (число?) ноль; - person MerickOWA; 01.05.2014
comment
Просто примечание: C # 6 не поставлялся с этой функцией, поэтому не пытайтесь объявлять var inline (stackoverflow.com/questions/31724163/) - person chris84948; 10.08.2016
comment
В C # 7 есть, и я пояснил ответ, чтобы показать синтаксис до и после 7. - person krillgar; 01.11.2017

[Обновлено для использования современного C # в соответствии с предложением @ sblom]

У меня была эта проблема, и я закончил с этим (в конце концов, if и 2 returns слишком длинны!):

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

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

person Duckboy    schedule 28.10.2008
comment
Не совсем уверен, что это на самом деле приведет к чему-то, что лучше работает после компиляции. - person BuZz; 05.11.2013
comment
Еще более кратко в C # 7: удалите строку int i; и просто используйте return int.TryParse (val, out var i) ? (int?) i : null; - person sblom; 02.03.2017
comment
Так что для полноты ;-) int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null; - person Duckboy; 02.06.2017
comment
С C # 6 это может быть сокращено до 1 строки: return int.TryParse (value, out var result)? результат: (int?) null; - person MeanGreen; 12.01.2018

C# < 7.1

с использованием оператора по умолчанию

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

C# >= 7.1

с использованием литерала по умолчанию

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

См. Управление версиями языка C # чтобы узнать, какую языковую версию поддерживает ваш проект

Редактировать:

См. Для справки снимок экрана ниже (C # Interactive):  введите описание изображения здесь

Источники:

person Jaa H    schedule 24.10.2018
comment
как это могло работать? Tryparse не будет работать или переменные, допускающие значение NULL, а f в вашем примере должно быть NULL. - person John Lord; 06.02.2020
comment
Не могли бы вы прояснить, что вы имеете в виду @JohnLord - person Jaa H; 24.03.2020
comment
tryparse ожидает, что он будет помещен в переменную, не допускающую значения NULL, поэтому разве ваше значение по умолчанию (int?) не заставит var иметь значение NULL? - person John Lord; 24.03.2020
comment
TryParse возвращает bool, условный оператор используется для оценки результата, если true, присваивает вывод, иначе назначает тип по умолчанию, если вы используете Nullable.GetUnderlyingType, вы увидите, что это null. - person Jaa H; 10.03.2021
comment
@JohnLord Я обновил свой ответ скриншотом из C # Interactive, надеюсь, это сделает его намного понятнее. - person Jaa H; 11.03.2021

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

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

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

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

person Michael    schedule 25.06.2011
comment
Я тестировал это для целых чисел, и это намного медленнее, чем int.TryParse ((string) value, out var result)? результат: по умолчанию (число?); - person Wouter; 21.10.2018

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

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

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

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

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

person mortb    schedule 22.03.2011
comment
В том-то и дело - если строка не может быть проанализирована до int, она должна возвращать null, а не генерировать исключение. - person svick; 29.03.2011
comment
если значение не является числовым, int.Parse выдает исключение, что не то же самое, что возврат null. - person an phu; 18.03.2016

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

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

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

person Lyskespark    schedule 21.06.2011

Все остальные ответы можно забыть - есть отличное универсальное решение: 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();
person Pavel Hodek    schedule 10.11.2011
comment
Это действительно очень полезно. На мой взгляд, это должно быть в стандартных библиотеках C #, потому что преобразования очень распространены в каждой программе;) - person BigChief; 28.04.2013
comment
Это очень приятно и полезно, НО я могу добавить, что это очень медленно, когда нужно делать преобразования для каждого элемента в большой коллекции элементов. Я протестировал 20000 элементов: при таком подходе преобразование 8 свойств каждого элемента занимает до 1 часа для завершения всей коллекции. С теми же образцами данных, но с использованием подхода Мэтта Гамильтона, для завершения потребуется всего несколько секунд. - person zed; 07.04.2016

Следующее должно работать для любого типа структуры. Он основан на коде Мэтт Манела с форумов MSDN. Как указывает Мерф, обработка исключений может быть дорогостоящей по сравнению с использованием специального метода TryParse для типов.

        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);
person Daniel Ballinger    schedule 08.04.2010

Я бы предложил следующие методы расширения для синтаксического анализа строки в значение 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();
        }
person Aleksandr Neizvestnyi    schedule 12.03.2018
comment
Уже есть так много ответов, и они получили даже много голосов. Вы действительно думаете, что ваш ответ нужен и придает новое качество этому посту? - person L. Guthardt; 12.03.2018
comment
@ L.Guthardt Да, я так думаю. На мой взгляд, мой ответ предлагает более универсальный способ решения проблемы, описанной в вопросе. Спасибо. - person Aleksandr Neizvestnyi; 12.03.2018

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

Нет.

person Orion Edwards    schedule 28.10.2008
comment
Считаете ли вы это прямым подходом? stackoverflow.com/a/6474962/222748 - person Michael; 29.11.2011

Это универсальное решение без накладных расходов на отражение.

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);
}
person Qi Luo    schedule 19.11.2013
comment
Думаю, можно заменить IsNullOrEmpty на IsNullOrWhitespace - person NibblyPig; 21.07.2016

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

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

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 ‹>, но, по крайней мере, на этот раз компилятор не требует явного экземпляра.

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

person orcun    schedule 24.03.2011

Я нашел и адаптировал код для класса Generic NullableParser. Полный код находится в моем блоге 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;
        }
    }
}
person John Dauphine    schedule 23.10.2009
comment
404 Не Найдено. просто давать ссылку - не лучшая практика - person Dirty-flow; 08.01.2013
comment
извините за это обновление @ Dirty-flow с полным кодом. Лучше поздно, чем никогда :) - person John Dauphine; 05.03.2016

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

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

person Murph    schedule 05.09.2008

Используя делегаты, следующий код может обеспечить возможность повторного использования, если вам потребуется синтаксический анализ, допускающий значение 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));
    }
  }
person umbyersw    schedule 07.09.2009

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

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

?

person Leigh Bowers    schedule 11.03.2010
comment
Вы можете, но тогда вы получите исключение, если stringVal имеет неправильный формат. См. Документацию int.Parse: msdn.microsoft.com/en-us/library /b3h1hf19.aspx - person Alex; 26.01.2011

Я придумал этот, который удовлетворил мои требования (я хотел, чтобы мой метод расширения как можно ближе имитировал возврат 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;
}
person wmoecke    schedule 20.03.2015

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

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? Age класса человека):

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

OR

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

person    schedule
comment
если myString не является числовым, int.Parse выдает исключение, что не то же самое, что возврат null. - person an phu; 18.03.2016