哇,TryParse是什么?

4

我有一个包含特定整数值的会话,这些值是用给定的控件索引的。通常,以下内容可以正常工作:

int value;
int.TryParse(Session["Key"].ToString(), out value);

然而,我需要考虑到“null”的情况。如果字符串无法通过默认的“out”参数返回,则会返回“null”。但是我注意到,“int.TryParse”无法处理:
int? value = null;
int.TryParse(Session["Key"].ToString(), out value);

那么如果解析失败会导致空值,你该如何尝试这个解析呢?
我找到了这个问题,微软开发者网络指出:
当此方法返回时,如果转换成功,则包含 s 中包含的数字的等效有符号整数值,否则为零。如果字符串参数是 null 或 String.Empty、格式不正确或表示小于 Min Value 或大于 Max Value 的数字,则转换失败。此参数未初始化传递。
这明确说明,如果 int.TryParse 失败,整数将保持零值。在我的用例中,零可能是有效值。所以我需要 null,有什么想法吗?

1
为什么常规转换不起作用?(int?)(Session["Key"]) - Alexei Levenkov
@AlexeiLevenkov 我可以尝试使用 Session["Key"] as int?,如果失败则返回可空。 - Greg
我有点困惑 - 我假设你将可空类型放入会话状态中 - 所以会有有效的 int? 值或缺少的键 - 两者都应该能够成功地进行转换/强制类型转换。我没有看到任何“失败”的情况... - Alexei Levenkov
@AlexeiLevenkov 你是正确的,但其他开发人员可能会出现“坏数据”。虽然他们不应该这样做。 - Greg
@AlexeiLevenkov,使用普通转换的另一个潜在问题是,如果它失败了,会在IIS内部引发异常,这是您不希望用户看到的。 - Greg
3个回答

9

当然可以;利用int.TryParse的返回值(返回转换是否成功):

int? retValue = null;
int parsedValue = 0;

if (int.TryParse(Session["Key"].ToString(), out parsedValue))
    retValue = parsedValue;
else
    retValue = null;

return retValue;

我承认这有点啰嗦,但你可以将它封装在一个函数中。


6
int tmp;
int? value = int.TryParse(Session["Key"].ToString(), out tmp) ? (int?)tmp : null;

当键不存在或键具有“null”值时,此代码显然会引发NullReferenceException(与OP想要的完全相同的行为,但并非其他人可能需要的行为。请注意)。 - Alexei Levenkov
@AlexeiLevenkov 毫无疑问,但我想这超出了问题的范围。 - AlexD

2
问题在于单词“null”。它是什么意思?null可能意味着值无法确定,抛出了异常,仅仅是该值为空,或者其他一些上下文的含义。你的问题是一个完美的例子,因为你自己是任意地表明,在你看来,null表示字符串解析失败。
Microsoft的TryParse范例很好,但只适用于有限的使用场景。考虑以下情况:
  • string == "89"
  • string == null
  • string == "Hello World"
  • string == ""
  • string == "2147483650"
然而,你唯一的选择是将整数或空值分配给输出,并返回true或false。
假设它起作用了,那么你会用这些信息做什么?像这样:
int? value = null;
if (int.TryParse(Session["Key"].ToString(), out value)) {
    if (value == null)
        // Handle "Appropriate" null
    else
        // Handle appropriate numeric value
}
else {
    // Note: value == null here, and TryParse failed
    // Handle null...
    // What if the reason it failed was because the number was too big?
    // What if the string was Empty and you wanted to do something special?
    // What if the string was actually junk?  Like "(423)322-9876" ?
    // Long-Story Short: You don't know what to do here without more info.
}

考虑一下这个 NullableInt TryParse 的例子:
public bool TryParseNullableInt(string input, out int? output)
{
    int tempOutput;
    output = null;
    if (input == null) return true;
    if (input == string.Empty) return true; // Would you rather this be 0?

    if (!int.TryParse(input, out tempOutput))
        return false; // What if string was "2147483650"... or "Twenty Three"?
    output = tempOutput;

    return true;
}

一个解决方案是使用枚举类型的 TryParse 而不是布尔类型的 TryParse:

public ParseStatus TryParseNullableInt(string input, out int? output)
{
    int tempInteger;
    output = null;
    if (input == null) return ParseStatus.Success;
    if (input == string.Empty) { output = 0; return ParseStatus.Derived; }

    if (!int.TryParse(input, out tempInteger)) {
        if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
            output = tempInteger;
            return ParseStatus.Derived;
        }
        long tempLong;
        if (long.TryParse(input, out tempLong))
            return ParseStatus.OutOfRange;
        return ParseStatus.NotParsable;
    }
    output = tempInteger;

    return ParseStatus.Success;
}

另一个问题是存在着 out 变量。第三个选择是使用一个描述性的单子,类似于这样:

public Maybe<int?> TryParseNullableInt(string input)
{
    if (input == null) return Maybe.Success(null);
    if (input == string.Empty) { return Maybe.Derived(0); }

    int tempInteger;
    if (!int.TryParse(input, out tempInteger)) {
        if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
            return Maybe.Derived(tempInteger);
        }
        long tempLong;
        if (long.TryParse(input, out tempLong))
            return Maybe.OutOfRange();
        return Maybe.NotParsable();
    }

    return Maybe.Success(tempInteger);
}

您可以将单一的可枚举值视为单子,或者像这样使用它们:
Maybe<int?> result = TryParseNullableInt("Hello");
if (result.HasValue) {
    if (result.Status == ParseStatus.Success)
        // Do something you want...
    else if (result.Status == ParseStatus.Derived)
        // Do something else... more carefully maybe?
}
else if (result.Status == ParseStatus.OutOfRange)
    MessageUser("That number is too big or too small");
else if (result.Status == ParseStatus.NotParsable)
    // Do something

通过使用单子(Monads),可能还有枚举的 TryParses,您现在可以从描述性返回中获得所有所需信息,而无需猜测 null 可能意味着什么。


太棒了!我刚刚在考虑用Maybe Monad来替换TryParse模式。我认为“out”参数增加了不必要的复杂性。对于更简单的情况,我甚至使用了一个命名元组,现在有更好的支持。(success, result) = FuncName(..); public (bool success, Subject result) FuncName (args..) {} - proximab

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