为什么C#编译器授权"抛出ex"在catch中,是否存在"抛出"有用吗?

时间:2014-07-10 13:23:25

标签: c# exception exception-handling

在C#中,年轻的开发者经常使用"抛出ex"而不是" throw"向父方法抛出异常。

示例:

try
{
    // do stuff that can fail
}
catch (Exception ex)
{
    // do stuff
    throw ex;
}

"扔前"这是一种不好的做法,因为堆栈跟踪在失败的方法下被截断。因此调试代码更加困难。所以代码必须是:

try
{
    // do stuff that can fail
}
catch (Exception ex)
{
    // do stuff
    throw;
}

我的问题是为什么编译器会对此进行授权(或者没有显示警告信息?)是否存在"抛出"有用吗?

4 个答案:

答案 0 :(得分:15)

  

是否存在"抛出"有用吗?

当然 - 有时你希望截断堆栈跟踪 - 以避免暴露实现细节等。其他时候你可能想要抛出 new 异常,这意味着编译器必须区别于重新抛出捕获的异常并抛出新的异常。

那么为什么你希望编译器阻止你做的事情1)不是非法的,2)可能有用吗?

答案 1 :(得分:4)

throw new Exception();throw ex;都将使用相同的语言规则来允许抛出异常对象(无论是新的还是现有的)。当您想要添加一些额外信息时,该选项很有用。

请参阅:如何:Explicitly Throw Exceptions - MSDN

  

您可以使用throw语句显式抛出异常。您   也可以使用throw语句再次抛出一个捕获的异常。   将信息添加到重新抛出的异常以便在调试时提供更多信息是一种很好的编码实践。

由于throw new Exception()throw ex;都需要相同的语言规则,因此区分这两者并不是编译器的工作。

简单地抛出现有异常而不对异常对象进行任何修改将使用相同的语言构造。

正如@D Stanley在他的answer中指出的那样,截断堆栈跟踪可能是理想的行为。

关于编译器没有警告它的问题,编译器的工作不是警告不良做法,而是有代码分析工具。例如,托管代码分析工具将引发throw ex; CA2200: Rethrow to preserve stack details

的警告

答案 2 :(得分:1)

虽然编译器当然可以防止一些明显的编程错误,但他们不可能在不引发一些不可避免的误报的情况下注意这样的最佳实践。

程序员可以选择在异常处理程序中更改异常的内容,或者抛出一个全新的异常。在这两种情况下,警告从异常处理程序中抛出异常的消息都会令人讨厌并且无用。

当您从递归函数中抛出异常时,会发生更改异常内部状态的一种情况。考虑一个递归下降解析器,从递归链中的几个层报告错误。每个级别的调用都可能为异常添加更多有用的信息。但是,在这种情况下,将每个后续层的异常包装到新的异常中是不切实际的,因为最终会得到表示平面列表的递归数据结构。对于这种情况,一种可行的解决方案是创建一个自定义异常,每个捕获者可以在重新抛出之前添加更多细节。由于函数(或更确切地说,一组函数)是递归的,因此抛出原始异常的代码中的位置不如首先导致异常的上下文的完整性重要。

这并不意味着找到这样的情况完全没用:代码校对工具,如ReSharper,当然可以帮助程序员注意这样的问题。但是,编译器对于最佳实践监视程序来说是一个糟糕的选择,因为在大多数情况下,编译器应该按照它所说的去做。

答案 3 :(得分:0)

我想我认为catch是罪魁祸首,当你实际上只想要一些东西传播到堆栈时。不应该catch完全避免使用finally吗?

bool bSucceeded = false;
try
{
    // do stuff that can fail
    bSucceeded = true;
}
finally
{
    if( !bSucceeded ) 
        // do stuff you need to do only on error.  (rare, for me)

    // cleanup stuff (you're nearly always doing this anyways right?)
}

我写了很多bSucceded,并且不认为我在<{1}}内编写throw而没有包装新的例外( )。 (至少自从我在99年左右开始学习Java以来​​。)

我猜测大量可能的方法来处理这个问题,这就是为什么他们让你在这里做你想做的事而不是试图锁定它。