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


300

Я хочу разобрать строку в обнуляемый 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?


1
Вы можете использовать string.IsNullOrEmpty (value), чтобы получить более ясную строку if.
Озгюр Каплан

Подумайте об использовании преобразования дженериков stackoverflow.com/questions/773078/…
Майкл Фрейдгейм

Ответы:


352

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есть способ разбора строки в Интс.


82
на одну строку меньше: вернуть Int32.TryParse (s, out i)? я: ноль;
Крис Кричит

2
«а» вернет ноль, но это не int и должно выдавать исключение
Арсен Мкртчян

54
@Chris, компилятору не нравится ваш встроенный оператор if (эти типы несовместимы: 'int': 'null'). Мне пришлось изменить его так: вернуть Int32.TryParse (s, out i)? (int?) i: null;
death_au

8
Int32 - это просто псевдоним int. Я бы использовал int.TryParse, чтобы сохранить типы, используемые в выравнивании. Если / когда int используется для представления другого целого числа битовой длины (что произошло), Int32 не будет совпадать с int.
Ричард Коллетт

4
вернуть int.TryParse (s, out i)? (int?) i: null;
Ник Шпрайцер

178

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

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

4
Я думаю, это зависит от вашего взгляда на условного оператора. Моя ментальная модель состоит в том, что это в значительной степени синтаксический сахар для эквивалента if-else, и в этом случае моя версия и версия Мэтта близки к идентичности, поскольку он более явный, а мой - больше cmopact.
McKenzieG1

11
Здесь нет побочного эффекта порядка оценки. Все шаги явно упорядочены и правильны.
Джон Ханна

22
возвращениеint.TryParse(val, out i) ? i : default(int?);
Барт Каликсто

7
"Ответ" @ Барта здесь лучший!
Андре Фигейредо

4
И теперь в C # 6 это может быть одна строка! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA

34

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

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

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

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


3
Не совсем уверен, что это на самом деле приведет к чему-то более эффективному после компиляции
BuZz

1
Еще более лаконично в C # 7: удалите int i;строку и просто продолжайтеreturn int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
Так что для полноты ;-) int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

В C # 6 это можно уменьшить до 1 строки: вернуть int.TryParse (value, out var result)? результат: (int?) null;
MeanGreen

16

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

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

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

Этот подход все еще может использоваться для не обнуляемых разборов, а также обнуляемых:

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 .


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

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

Источники:


как это могло работать? Tryparse не будет работать или обнулять переменные, и f в вашем примере должно быть обнуляемым.
Джон Лорд

Не могли бы вы уточнить, что вы имеете в виду @JohnLord
Jaa H

Ожидается, что tryparse будет помещен в ненулевую переменную, поэтому не будет ли ваша стандартная (int?) принудительная переменная var быть обнуляемой?
Джон Лорд

@JohnLord, может быть, это поможет вам понять, что происходит на stackoverflow.com/questions/3632918/…
Jaa H

9

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

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

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


1
В том-то и дело - если строку невозможно проанализировать int, она должна возвращать null, а не выдавать исключение.
svick

1
если значение не числовое, int.Parse выдает исключение, которое не совпадает с возвращением null.
Пху

8

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

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

5

Я чувствую, что мое решение очень чистое и приятное:

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 и т. Д.


5

Вы можете забыть все остальные ответы - есть отличное общее решение: 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();

1
Это действительно очень полезно. По моему мнению, это должно быть в стандартных библиотеках c #, потому что преобразования очень распространены в каждой программе;)
BigChief

Это очень приятно и полезно, НО я могу добавить, что это очень медленно, когда нужно выполнить преобразования для каждого элемента в большой коллекции элементов. Я проверил с 20000 предметов: используя этот подход, преобразование 8 свойств каждого предмета занимает до 1 часа, чтобы закончить всю коллекцию. С теми же примерами данных, но с использованием подхода Мэтта Гамильтона, это займет всего несколько секунд, чтобы закончить.
Зед

3

Следующее должно работать для любого типа структуры. Он основан на коде Мэтта Манелы с форумов 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);

3

Я бы предложил следующие методы расширения для разбора строки в значение 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();
        }

Там уже так много и даже завышенных ответов. Вы действительно думаете, что ваш ответ необходим и добавляет новое качество в этот пост?
Л. Гутардт

1
@ L.Guthardt Да, я так думаю. Как мне кажется, мой ответ принесет более универсальный способ решения описанного вопроса. Спасибо.
Александр Неизвестный

2

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

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

Я думаю, что вы можете заменить IsNullOrEmptyнаIsNullOrWhitespace
NibblyPig


1

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

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

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 и т. Д.


1

Я нашел и адаптировал некоторый код для класса 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;
        }
    }
}

1
404 Не Найдено. не стоит просто давать ссылку
Dirty-flow

извините за это @ Dirty-flow обновление с полным кодом. Лучше поздно, чем никогда :)
Джон Дофин

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
если myString не является числовым, int.Parse генерирует исключение, которое не совпадает с возвращением null.
Пху

0

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

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


0

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


0

Я придумал этот, который удовлетворял моим требованиям (я хотел, чтобы мой метод расширения как можно ближе эмулировал возвращение TryParse фреймворка, но без блоков try {} catch {} и без жалоб компилятора на вывод обнуляемый тип в методе 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

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

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? Возрастное свойство класса person):

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); });
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.