Convert.ToInt32与TryParse的区别

5
我们都知道大量抛出异常对应用程序性能的影响,因此,我们应该避免使用异常来控制流程。
说完这句话后,我必须承认,在编码时我并没有太在意这个问题。我一直在Java平台上工作,但最近我在.NET平台上工作,刚刚发现了这个方便的方法:public static bool TryParse(string s,out int result),它允许你将一个字符串转换为整数而不引发异常。从那时起,我一直在使用它。我只是想问一下你对于使用public static bool TryParse(string s,out int result)public static int ToInt32(string value)的偏好。
从Java的角度来看,只是指出它缺少类似的方法,尽管我们可以通过以下方式获得它: boolean isInteger = Pattern.matches("^\d*$", myString); 谢谢。
5个回答

7
是的,Java缺少类似的方法,尽管没有“out”参数,但想要返回基本类型却相当困难。一般来说,在C#中,如果你预期值有可能不是整数,则应使用“TryParse”,否则应使用“ToInt32”;这样异常情况就可以被正确处理。
特别是如果性能是你想要“TryParse”的主要原因,那么你发帖的正则表达式匹配方法会更糟糕。异常的性能开销(实际上非常小)被错误地使用时,会混淆控制流的易于理解程度。

顺便提一下,在Java中有其他获取它的方法。其中一个是使用Integer而不是int,另一个是将int放入数组中并将其传递给方法。 - Juan Carlos Blanco Martínez
是的,这就是为什么我提到了“想要返回一个原始类型”的部分。虽然可能实现,但从本质上来说并不简洁。 - Calum

3
我不知道C#,但在Java中,异常只有在实际抛出时才会显得很昂贵。如果你预计有相当一部分字符串是无效的,那么最好在使用正则表达式之前先进行验证,即使你使用正则表达式。但不要使用String.matches()或Pattern.matches()来应用正则表达式;这些方法每次调用时都会重新编译正则表达式。相反,提前编译正则表达式并将其保存为Pattern对象,然后使用它进行验证。在我的测试中,解析包含10,000个字符串的列表,其中20%是无效的,通过Pattern进行预验证几乎比仅使用Integer.parseInt()并捕获异常快两倍。
但是,这种讨论仅适用于在紧密循环中执行大量转换的情况。如果你只偶尔进行转换,比如接受用户输入时,让Integer.parseInt()进行验证就可以了。如果你选择使用正则表达式进行验证,你需要一个比^\d*$更好的正则表达式——该正则表达式也会匹配空字符串以及大于Integer.MAX_VALUE的“数字”,而且它根本不匹配负数。

1

在Java中,您可以使用广泛使用的StringUtils(在commons-lang上)来实现此目的,该类具有一个名为isNumeric的方法。

也许您可以查看这些人为该函数编写的代码:

public static boolean isNumeric(String str) {
  if (str == null) {
    return false;
  }
  int sz = str.length();
  for (int i = 0; i < sz; i++) {
    if (Character.isDigit(str.charAt(i)) == false) {
      return false;
    }
  }
  return true;
 }

我并不是说这是最有效的方法,但是还有另一种选择可以避免使用正则表达式。祝你好运!


1
这个 "== false" 是什么鬼?我简直不敢相信我在 Apache 源代码中看到了这个! - Alan Moore
1
"== false" 的问题在于,如果你不小心只使用一个“=”,那会发生什么。表达式的类型仍然是布尔类型,所以它可以编译通过,但最终可能会导致一个微妙、间歇性的错误。这只是一个需要养成良好习惯的例子。 - Alan Moore
如果 ( !Character.isDigit(str.charAt(i)) ) - Adam Tolley
@AlanMoore,对于foo() == false的情况不是这样。您不能将值分配给方法的返回值。在其他情况下,例如foo == false,当然可能会出现问题。因此最好编写false == foo,这比true == (false == foo)稍微好一些。 - Robert

1

And from the point of view of Java, just pointing that it's missing such a similar method, despite we could get it through things like:

boolean isInteger = Pattern.matches("^\d*$", myString);

要预测Integer.parseInt(myString)是否会抛出异常,还有更多的工作要做。字符串可能以-开头。此外,int不能超过10个有效数字。因此,更可靠的表达式应该是^-?0*\d{1,10}$。但即使这个表达式也无法预测每个异常,因为它仍然太不精确。

生成可靠的正则表达式是可能的。但它会非常长。也可以实现一个方法来精确确定是否会抛出parseInt异常。它可能看起来像这样:

static boolean wouldParseIntThrowException(String s) {
    if (s == null || s.length() == 0) {
        return true;
    }

    char[] max = Integer.toString(Integer.MAX_VALUE).toCharArray();
    int i = 0, j = 0, len = s.length();
    boolean maybeOutOfBounds = true;

    if (s.charAt(0) == '-') {
        if (len == 1) {
            return true; // s == "-"
        }
        i = 1;
        max[max.length - 1]++; // 2147483647 -> 2147483648
    }
    while (i < len && s.charAt(i) == '0') {
        i++;
    }
    if (max.length < len - i) {
        return true; // too long / out of bounds
    } else if (len - i < max.length) {
        maybeOutOfBounds = false;
    }
    while (i < len) {
        char digit = s.charAt(i++);
        if (digit < '0' || '9' < digit) {
            return true;
        } else if (maybeOutOfBounds) {
            char maxdigit = max[j++];
            if (maxdigit < digit) {
                return true; // out of bounds
            } else if (digit < maxdigit) {
                maybeOutOfBounds = false;
            }
        }
    }
    return false;
}

虽然我不知道哪个版本更有效率,但它主要取决于上下文需要什么样的检查。

在C#中,如果要“检查”字符串是否可以转换,您将使用TryParse。如果返回true,则作为副产品同时进行了转换。这是一个很棒的功能,我认为只需重新实现parseInt以返回null而不是抛出异常即可。

但是,如果您不想重新实现解析方法,仍然可以方便地提供一组可以根据情况使用的方法。它们可能看起来像这样:

private static Pattern QUITE_ACCURATE_INT_PATTERN = Pattern.compile("^-?0*\\d{1,10}$");

static Integer tryParseIntegerWhichProbablyResultsInOverflow(String s) {
    Integer result = null;
    if (!wouldParseIntThrowException(s)) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
            // never happens
        }
    }
    return result;
}

static Integer tryParseIntegerWhichIsMostLikelyNotEvenNumeric(String s) {
    Integer result = null;
    if (s != null && s.length() > 0 && QUITE_ACCURATE_INT_PATTERN.matcher(s).find()) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        // only happens if the number is too big
        }
    }
    return result;
}

static Integer tryParseInteger(String s) {
    Integer result = null;
    if (s != null && s.length() > 0) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        }
    }
    return result;
}

static Integer tryParseIntegerWithoutAnyChecks(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException ignored) {
    }
    return null;
}

0
我只是想问一下您对于使用public static bool TryParse(string s,out int result)或public static int ToInt32(string value)的偏好。

是的,除非我期望值始终有效,否则我会使用TryParse。我发现这样比使用异常更清晰。即使我想要一个异常,我通常也想要自定义消息或抛出自己的自定义异常;因此,我使用TryParse并手动抛出异常。

在Java和C#中,我都尽量捕获最少的异常集合。在Java中,这意味着我必须分别捕获NullPointerException和NumberFormatException以响应Number.ValueOf(...); 或者,我可以捕获“Exception”并冒着捕获意外内容的风险。在C#中使用TryParse,我根本不用担心这个问题。


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