Як розбити рядок на нульовий 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;
        }
    }
}   

Чи є кращий спосіб зробити це?

EDIT: Дякую за пропозиції TryParse, я знав про це, але це вийшло приблизно так само. Мені більше цікаво дізнатися, чи існує вбудований фреймворковий метод, який буде розбиратися безпосередньо в нульовий int?


1
Ви можете використовувати string.IsNullOrEmpty (значення), щоб зробити рядок if чіткішим.
Özgür Kaplan

Розглянемо використання дженериків перетворення stackoverflow.com/questions/773078 / ...
Майкл Freidgeim

Відповіді:


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)? i: null;
Chris Shouts

2
"a" поверне нульове значення, але воно не є int і повинно кинути виняток
Arsen Mkrtchyan

54
@Chris, компілятору не подобається ваш рядок, якщо оператор (Ці типи не сумісні: 'int': 'null'). Мені довелося внести зміни до цього: return Int32.TryParse (s, out i)? (int?) i: null;
смерть_ау

8
Int32 - це лише псевдонім до int. Я б використав int.TryParse, щоб зберегти типи, які використовуються у вирівнюванні. Якщо / коли int використовується для представлення іншого цілої довжини бітів (що трапилося), Int32 не вирівняється з int.
Річард Коллетт

4
повернути int.TryParse (s, out i)? (int?) i: null;
Нік Спріцєр

177

Ви можете зробити це в одному рядку, використовуючи умовний оператор і той факт, що ви можете 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;

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

11
Тут немає жодного побічного ефекту порядку оцінки. Усі кроки чітко впорядковані та правильні.
Джон Ханна

22
поверненняint.TryParse(val, out i) ? i : default(int?);
Барт Калікто

7
@ "Відповідь" Барта тут найкращий!
Andre Figueiredo

4
А тепер у С # 6 це може бути один рядок! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA

34

[ Оновлено, щоб використовувати сучасний C # за пропозицією @ sblom]

У мене була ця проблема, і я закінчився цією проблемою (зрештою, ifі 2 returns - це довгодушність!):

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

Що стосується більш серйозної уваги, намагайтеся не змішувати int, що є ключовим словом C # Int32, що є типом BCL .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;
Дакбой

За допомогою C # 6 це може бути зменшено до 1 рядка: повернути int.TryParse (значення, вихідний результат)? результат: (int?) null;
MeanGreen

16

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

Існує такий підхід, який буде аналізувати безпосередньо нульовий 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); }
}

Цей підхід як і раніше може бути використаний як для ненульових синтаксичних аналогів, так і для змінних:

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 .


Я перевірив це на цілі числа, і це набагато повільніше, ніж значення int.TryParse ((рядок), результат вар)? результат: за замовчуванням (int?);
Wouter

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

Джерела:


як це могло працювати? Tryparse не працюватиме або змінює змінні змінні, і f у вашому прикладі має бути нульовим.
Іван Лорд

Скажіть, будь ласка, що ви маєте на увазі @JohnLord
H

tryparse розраховує включити змінну, що не зводиться до індексу, тож чи не змусить ваш var за замовчуванням (int?) бути змінним?
Іван Лорд

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

Звичайно, це загальне рішення, яке вимагає лише, щоб аргумент generics мав статичний метод "Розбір (рядок)". Це працює для чисел, булевих, 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 #, тому що переходи дуже поширені в кожній програмі;)
BigChaja

Це дуже приємно і корисно, Але я можу додати, що це надзвичайно повільно, коли потрібно робити перетворення для кожного елемента у великій колекції предметів. Я перевірив 20000 предметів: використовуючи цей підхід, перетворення 8 властивостей кожного предмета займає до 1 години, щоб закінчити всю колекцію. З тими ж даними вибірки, але, використовуючи підхід Метта Гамільтона, потрібно лише кілька секунд.
зед

3

Наступне має працювати для будь-якого типу структури. Це заснований на коді Метт Манела з форумів 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);

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, оскільки його компілятор не може зробити. Я також повинен був визначити делегата, оскільки ключове слово не може використовуватися у 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 Не знайдено. це не найкраща практика, аби лише надати посилання
Брудний потік

вибачте за це оновлення @ брудний потік з повним кодом. Краще пізно, ніж ніколи :)
Джон Дофін

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, але без спроб {} блоків {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;
}

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

Використовуйте цей метод розширення в коді (fill int? Age властивості класу 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.