优雅的TryParse

91

我觉得每次使用 TryParse 都会导致代码变得有些丑陋。主要是我这样使用:

int value;
if (!int.TryParse(someStringValue, out value))
{
    value = 0;
}

有没有更优雅的解决方案来解析所有基本数据类型,具体来说,是否有一种方法可以在一行中进行安全失败解析?通过安全失败,我假设在解析失败时设置默认值而不会引发异常。

顺便说一下,这是为必须执行某些操作即使解析失败的情况,只使用默认值。


2
int 的默认值为 0,并且与 out 参数一起使用,但无论如何:https://dev59.com/OkjSa4cB1Zd3GeqPD0XC#1078521 - Tim Schmelter
@Tim 有时候并不是类型的默认值,这并不是特指整数,还包括其他数据类型。 - Antonio Bakula
@AntonioBakula:那么请使用我链接中Jon Skeet的方法,并在您自己的TryParse中使用ref参数。 - Tim Schmelter
9个回答

235

如果您喜欢单行代码,这是有效的并且可能更适合您:

int i = int.TryParse(s, out i) ? i : 42;

如果无法解析字符串s,则将i的值设置为42,否则将i = i


10
我必须承认我不知道它能在一行中工作。我本来以为至少VB.NET不允许,但是这个代码:Dim i As Int32 = If(Int32.TryParse("nonum", i), i, 42)。谢谢。 - Tim Schmelter
3
我非常喜欢这个答案! - Envil
7
如果您想直接返回结果:return int.TryParse(s, out int i) ? i : 42; - BalintPogatsa
1
这有点冗余。你只想在TryParse返回false时执行额外的赋值,但三元运算符要求你考虑两种情况。 - Kyle Delaney
2
请注意,@BalintPogatsa的更新版本需要C# 7.0。 - freedomn-m
我认为应该这样写:int i = (int.TryParse(s, out i)) ? i : 42; - Khalid Almannai

24

直接使用扩展方法怎么样?

public static class Extensions
{
    public static int? TryParse(this string Source)
    {
        int result;
        if (int.TryParse(Source, out result))
            return result;
        else

            return null;
    }
}

使用新的C#语法,在单行中:

public static int? TryParse(this string Source) => int.TryParse(Source, out int result) ? result : (int?)null;

使用方法:

v = "234".TryParse() ?? 0

我更喜欢这个答案,即使可能只是个人偏好的问题,而不是@skarmats的答案。 - Antonio Bakula
5
是的,偏好总是很重要。但是,您将无法对返回值进行类型推断。因此,您将会用许多方法(每种要解析的类型一个方法)来污染String-IntelliSense。 - skarmats
示例实现在此处:https://ericlippert.com/2012/08/14/out-parameters-and-linq-do-not-mix/ :static int? MyTryParse(this string item) { int tmp; bool success = int.TryParse(item, out tmp); return success ? (int?)tmp : (int?)null; } - Jon
1
为了节省您的输入时间,这里是最常见的原始类型的扩展类:https://hastebin.com/eleyawonex.cs - djk

15
你可以编写自己的方法来解决问题,以适合你自己的需求。我之前偶然发现了包装了 TryParse 方法的 Maybe 类。
int? value = Maybe.ToInt("123");

if (value == null)
{
    // not a number
}
else
{
    // use value.Value
}

或者在行内指定默认值:

int value = Maybe.ToInt("123") ?? 0;

请注意 Nullable<int>/int?int 之间的区别。

更多信息请参见http://www.kodefuguru.com/post/2010/06/24/TryParse-vs-Convert.aspx


2
请提供您提到的 Maybe 类的链接。 - cja
答案中的链接是404。 - cja
请注意,这里有一个很好的Maybe实现的微型库:https://github.com/vkhorikov/CSharpFunctionalExtensions - solvingJ
原始页面已缓存于https://web.archive.org/web/20100630161315/http://www.kodefuguru.com/post/2010/06/24/TryParse-vs-Convert.aspx。KodefuGuru的Maybe类代码也可以在GitHub上找到:https://github.com/kodefuguru/kodefu/blob/master/Kodefu/Maybe.cs。 - pholpar

13

C# 7中有一个不错的小功能,声明表达式,所以在C# 7中可以用如下语法代替原来的写法:

int x;
if (int.TryParse("123", out x))
{
  DoSomethingWithX(x);
}

我们可以使用:

if (int.TryParse("123", out int x))
{
  DoSomethingWithX(x);
}

对我来说很不错 :)


7
遗憾的是,声明表达式没有被包括在C# 6的最终版本中。 - dav_i
1
@dav_i 但是现在我们有了C#7!哈利路亚! - Eric Sondergard
4
@EricSondergard 这确实是一次情感上的过山车之旅。 - dav_i
2
这很不错,但还缺少默认值。在这里,我们只是避免了声明行。 - Andrew

4

在一行中使用C#7

int.TryParse(s, out var i) ? i : (int?)null;

示例方法:

public static int? TryParseSafe(string s)
{
    return int.TryParse(s, out var i) ? i : (int?)null;
}

3
在您的具体示例中,您可以这样做:
int value; 
int.TryParse(someStringValue, out value);

因为 Int32.TryParse() 的文档中说明,如果解析失败,value 会被设置为0。


给 Tim 的留言:有时候不是类型的默认值,我不是特指 int 类型,还有其他数据类型。 - Antonio Bakula

1
你可以使用TypeDescriptor代替:
public T Convert<T>(string input, T defaultVal)
{
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
    if(converter != null)
    {
        return (T)converter.ConvertFromString(input);
    }
    return defaultVal;
}

public T Convert<T>(string input)
{
    return Convert(input, default(T));
}

你可以将 T 约束为结构体,并使用 Nullable(如 @skarmats 的答案所示)。

在这种情况下,您可以始终使用可选参数,使得您的第一个签名为 Convert<T>(string input, T defaultVal = default(T)),第二种方法可以被删除。 - dav_i

1

我对这个答案有一个小的改进建议,就是在下面的片段中创建一个扩展方法和TryConvert扩展方法:

public static T Convert<T>(this string input, T defaultVal)
{
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if (converter != null)
    {
        return (T) converter.ConvertFromString(input);
    }
    return defaultVal;
}

public static T Convert<T>(this string input)
{
    return Convert(input, default(T));
}

public static bool TryConvert<T>(this string input, T defaultVal, out T result)
{
    try
    {
        result = Convert(input, defaultVal);
        return true;
    }
    catch (Exception exception)
    {
        result = defaultVal;
        return string.IsNullOrEmpty(input);
    }
}

然后我们可以像片段一样使用它

 if (someValue.TryConvert(1, out var result))
 {

 }

result 数据类型会根据 defaultVal 自动确定。


0

对于尝试使用F#的C#开发人员来说,这是一个令人惊喜的好消息。TryParse方法返回一个包含布尔值和值的元组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接