关于c#:如何将字符串解析为可空的int

关于c#:如何将字符串解析为可空的int

How to parse a string into a nullable int

我想在C#中将字符串解析为可以为空的int。即。 我想要返回字符串的int值,如果无法解析,则返回null。

我有点希望这会奏效

1
int? val = stringVal as int?;

但这不起作用,所以我现在这样做的方式是我写了这个扩展方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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?


int.TryParse可能更容易:

1
2
3
4
5
6
public static int? ToNullableInt(this string s)
{
    int i;
    if (int.TryParse(s, out i)) return i;
    return null;
}

编辑@Glenn int.TryParse"内置于框架中"。它和int.Parse是将字符串解析为int的方法。


您可以使用条件运算符在一行中完成此操作,并且可以将null转换为可空类型(两行,如果您没有预先存在的int,则可以重用):

前C#7:

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

使用C#7的更新语法,您可以在方法调用中声明输出变量,这甚至更简单。

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

我有这个问题我最终得到了这个(毕竟,if和2 return s太啰嗦了!):

1
2
3
4
5
int? ParseNInt (string val)
{
    int i;
    return int.TryParse (val, out i) ? (int?) i : null;
}

更严重的是,尽量不要将int(这是一个C#关键字)和Int32(这是一个.NET Framework BCL类型)混合在一起 - 尽管它有效,但它只会使代码看起来很乱。


Glenn Slaven: I'm more interested in knowing if
there is a built-in framework method
that will parse directly into a
nullable int?

如果值有效(如null或空字符串),则此方法将直接解析为可空int(而不仅仅是int),但会为无效值引发异常,因此您需要捕获异常并返回默认值对于那些情况:

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

这种方法仍然可以用于非可空的解析以及可以为空的:

1
2
3
4
5
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格式时它仍然没有检查你的语言环境的问题,参见bug 93559。


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

资料来源:

  • 条件运算符

  • 缺省值为nullable int(在此stackoverflow帖子中解释)。

  • 瓦尔

老话题,但怎么样:

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

我更喜欢这个作为解析null的地方,TryParse版本不会抛出错误,例如ToNullableInt32(XXX)。这可能会引入有害的无声错误。


尝试这个:

1
2
3
4
5
6
7
public static int? ParseNullableInt(this string value)
{
    int intValue;
    if (int.TryParse(value, out intValue))
        return intValue;
    return null;
}

您可以忘记所有其他答案 - 有一个很好的通用解决方案:
http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

这允许你编写非常干净的代码,如下所示:

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

并且:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
2
3
4
5
6
7
8
9
10
11
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)"。这适用于数字,布尔值,日期时间等。


以下内容适用于任何结构类型。它基于来自MSDN论坛的Matt Manela的代码。正如Murph所指出的,与使用类型专用的TryParse方法相比,异常处理可能很昂贵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        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;
        }

这些是我使用的基本测试用例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        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值,并且能够在无法解析的情况下定义默认值:

1
2
3
4
5
6
7
8
9
10
11
12
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
2
3
4
5
6
7
8
9
10
11
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);
}

我找到并修改了Generic NullableParser类的一些代码。完整的代码在我的博客Nullable TryParse上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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;
        }
    }
}

I'm more interested in knowing if there is a built-in framework method that will parse directly into a nullable int?

没有。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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
2
3
var result ="123".ParseBy(int.Parse);

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

解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
    }
}

第一个版本较慢,因为它需要try-catch但它看起来更干净。如果使用无效字符串不会多次调用它,那就不那么重要了。
如果性能有问题,请注意在使用TryParse方法时,需要指定ParseBy的类型参数,因为编译器无法推断它。我还必须定义一个委托,因为out关键字不能在Func <>中使用,但至少这次编译器不需要显式实例。

最后,您也可以将它与其他结构一起使用,即decimal,DateTime,Guid等。


我已经想出了这个,它满足了我的要求(我希望我的扩展方法尽可能接近模拟框架的TryParse的返回,但没有try {} catch {}块,并且没有编译器抱怨推断a框架方法中的可空类型)

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

使用委托,如果您发现自己需要对多个结构类型进行可空的解析,则以下代码能够提供可重用性。我在这里展示了.Parse()和.TryParse()版本。

这是一个示例用法:

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

以下是让你在那里的代码......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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的变体解决了这个问题 - 如果你想要创造性(让你的代码看起来更优雅),你可以用3.5中的扩展方法做一些事情,但代码或多或少都是一样的。


我建议代码吼叫。当转换错误发生时,您可以使用异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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?person类的Age属性):

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

要么

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

我意识到这是一个古老的话题,但你不能简单地说:

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


推荐阅读