Я хочу преобразовать строку в обнуляемое 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?
Рассмотрите возможность использования универсального преобразования stackoverflow.com/questions/773078/…





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 - это способ то для синтаксического анализа строк в целые числа.
на одну строку меньше: return Int32.TryParse (s, out i)? я: ноль;
Я определенно предпочитаю этот способ TryParse
Я собирался опубликовать пример, указывающий на то, что возникнет исключение 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);
"a" вернет null, но это не int и должно вызвать исключение.
@Arsen ... именно это и задавал первоначальный вопрос. Посмотрите на его фрагмент кода.
@Chris, компилятору не нравится ваш встроенный оператор if (эти типы несовместимы: 'int': 'null'). Мне пришлось изменить его на: return Int32.TryParse (s, out i)? (int?) я: ноль;
Int32 - это просто псевдоним int. Я бы использовал int.TryParse, чтобы используемые типы были согласованы. Если / когда int используется для представления целого числа другой длины в битах (что произошло), Int32 не будет совпадать с int.
return int.TryParse (s, out i)? (int?) я: ноль;
общедоступный статический интервал? _ni (эта строка s) {int i; return int.TryParse (s, out i)? (int?) i: null;}
@death_au i: новый интервал? ()
@ccalboni тоже работает. Как и i : default(int?). Подумав об этом, в ответ он все равно передаст результат на int?, так что, может быть, лучше, если я просто сделаю это? Точно сказать не могу.
Еще одна строка меньше в C# 7 (встроенное объявление i): int.TryParse (s, out int i)? (int?) я: ноль;
общедоступный статический интервал? ToNullableInt (эта строка s) => (int.TryParse (s, out int i))? я: (число?) ноль;
Использование встроенного объявления и default с выводом типа int.TryParse(s, out var i) ? i as int? : default
Попробуй это:
public static int? ParseNullableInt(this string value)
{
int intValue;
if (int.TryParse(value, out intValue))
return intValue;
return null;
}
Вы можете сделать это в одной строке, используя условный оператор и тот факт, что вы можете привести 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;
Вы могли бы - но полагаться на побочные эффекты и подобный порядок оценки мерзко, когда вы можете сделать последовательную зависимость очевидной, используя синтаксис Мэтта. Между прочим, для этого и подобных выражений удобно использовать default (int?) Именно для того, чтобы избежать ошибок вывода типов, которые проистекают из нетипизированной природы null, без необходимости вставки приведений, которые могут фактически выполнять код.
Я думаю, это зависит от вашего взгляда на условный оператор. Моя ментальная модель состоит в том, что это в значительной степени синтаксический сахар для эквивалента if-else, и в этом случае моя версия и версия Мэтта близки к идентичности, причем он более явный, моя более cmopact.
Здесь нет побочного эффекта порядка оценки. Все шаги четко упорядочены и правильны.
вернуть int.TryParse(val, out i) ? i : default(int?);
@ "Ответ" Барта здесь лучший!
А теперь в C# 6 это может быть одна строка! Int32.TryParse (stringVal, out var tempVal)? tempVal: (число?) ноль;
Просто примечание: C# 6 не поставлялся с этой функцией, поэтому не пытайтесь объявить var inline (stackoverflow.com/questions/31724163/…)
В C# 7 есть, и я пояснил ответ, чтобы показать синтаксис до и после 7.
Вы должны никогда использовать исключение, если вам не нужно - накладные расходы ужасны.
Варианты TryParse решают проблему - если вы хотите проявить творческий подход (чтобы ваш код выглядел более элегантно), вы, вероятно, могли бы что-то сделать с помощью метода расширения в 3.5, но код будет более или менее таким же.
[Обновлено для использования современного C# согласно предложению @ sblom]
У меня была эта проблема, и я закончил с этим (в конце концов, if и 2 return - это так уж много!):
int? ToNullableInt (string val)
=> int.TryParse (val, out var i) ? (int?) i : null;
Если серьезно, постарайтесь не смешивать int, ключевое слово C#, с Int32, являющимся типом BCL .NET Framework - хотя это работает, код просто выглядит беспорядочным.
Не совсем уверен, что это на самом деле приведет к чему-то, что лучше работает после компиляции.
Еще более кратко в C# 7: удалите строку int i; и просто перейдите к return int.TryParse (val, out var i) ? (int?) i : null;
Так что для полноты ;-) int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
С C# 6 это может быть сокращено до 1 строки: return int.TryParse (value, out var result)? результат: (int?) null;
I'm more interested in knowing if there is a built-in framework method that will parse directly into a nullable int?
Нет.
Считаете ли вы это прямым подходом? stackoverflow.com/a/6474962/222748
Используя делегаты, следующий код может обеспечить возможность повторного использования, если вам потребуется синтаксический анализ, допускающий значение 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));
}
}
Я нашел и адаптировал код для класса 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;
}
}
}
404 Не Найдено. просто давать ссылку - не лучшая практика
извините за это обновление @ Dirty-flow с полным кодом. Лучше поздно, чем никогда :)
Я понимаю, что это старая тема, но не могли бы вы просто:
(Nullable<int>)int.Parse(stringVal);
?
Вы можете, но тогда вы получите исключение, если stringVal имеет неправильный формат. См. Документацию int.Parse: msdn.microsoft.com/en-us/library/b3h1hf19.aspx
Следующее должно работать для любого типа структуры. Он основан на коде Мэтт Манела с форумов 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);
Старая тема, а как насчет:
public static int? ParseToNullableInt(this string value)
{
return String.IsNullOrEmpty(value) ? null : (int.Parse(value) as int?);
}
Мне это больше нравится, так как требование, где разбирать null, версия TryParse не выдает ошибку, например. ToNullableInt32 (XXX). Это может привести к нежелательным скрытым ошибкам.
В том-то и дело - если строка не может быть проанализирована на int, она должна вернуть null, а не генерировать исключение.
если значение не является числовым, int.Parse выдает исключение, что не то же самое, что возврат null.
Я чувствовал, что должен поделиться своим, более общим.
Использование:
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 и т. д.
Я считаю, что мое решение - очень чистое и красивое решение:
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 и т. д.
Glenn Slaven: I'm more interested in knowing if there is a built-in framework method that will parse directly into a nullable 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); }
}
Этот подход все еще можно использовать для синтаксического анализа, не допускающего значения 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.
Я тестировал это для целых чисел, и это намного медленнее, чем int.TryParse ((string) value, out var result)? результат: по умолчанию (число?);
Все остальные ответы можно забыть - есть отличное универсальное решение: 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();
Это действительно очень полезно. На мой взгляд, это должно быть в стандартных библиотеках C#, потому что преобразования очень распространены в каждой программе;)
Это очень приятно и полезно, НО я могу добавить, что это очень медленно, когда нужно делать преобразования для каждого элемента в большой коллекции элементов. Я протестировал 20000 элементов: при таком подходе преобразование 8 свойств каждого элемента занимает до 1 часа для завершения всей коллекции. С теми же образцами данных, но с использованием Подход Мэтта Гамильтона, это займет всего несколько секунд.
Это универсальное решение без накладных расходов на отражение.
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
Я придумал этот, который удовлетворил мои требования (я хотел, чтобы мой метод расширения как можно ближе имитировал возврат TryParse фреймворка, но без блоков try {} catch {} и без жалоб компилятора на вывод тип, допускающий значение NULL, в рамках метода)
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 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;
}
если myString не является числовым, int.Parse генерирует исключение, что не то же самое, что возврат 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? Age класса человека):
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); });
Я бы предложил следующие методы расширения для синтаксического анализа строки в значение 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();
}
Уже есть так много ответов, и они получили даже много голосов. Вы действительно думаете, что ваш ответ нужен и придает новое качество этому посту?
@ L.Guthardt Да, я так думаю. На мой взгляд, мой ответ предлагает более универсальный способ решения проблемы, описанной в вопросе. Спасибо.
используя оператор по умолчанию
var result = int.TryParse(foo, out var f) ? f : default(int?);
используя литерал по умолчанию
var result = int.TryParse(foo, out var f) ? f : default;
See C# language versioning to ascertain what language version your project supports
См. Для справки снимок экрана ниже (C# Interactive):

Источники:
как это могло работать? Tryparse не будет работать или переменные, допускающие значение NULL, а f в вашем примере должно быть NULL.
Не могли бы вы прояснить, что вы имеете в виду @JohnLord
tryparse ожидает, что он будет помещен в переменную, не допускающую значения NULL, поэтому разве ваше значение по умолчанию (int?) не заставит var иметь значение NULL?
TryParse возвращает bool, условный оператор используется для оценки результата, если true, присваивает вывод, иначе назначает тип по умолчанию, если вы используете Nullable.GetUnderlyingType, вы увидите, что это null.
@JohnLord Я обновил свой ответ скриншотом из C# Interactive, надеюсь, это сделает его намного понятнее.
Вы можете использовать string.IsNullOrEmpty (value), чтобы сделать строку if более четкой.