为什么要捕获异常再次抛出?

13

在一个web服务中,我看到了这段代码:

<WebMethod()> _
Public Function dosomething() As Boolean
    Try
        If successful Then
            Return True
        Else
            Return False
        End If
    Catch ex As Exception
        Throw ex
    End Try
End Function

捕获异常然后再次抛出的意义何在?我有什么遗漏吗?

编辑: 感谢回答!我认为就是这样,但不确定是否可以/应该重构掉它们而没有任何影响。

9个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
47

不要这么做。

如果您确实需要重新抛出异常,只需使用throw;,而不是使用throw ex;。使用throw ex;会抹掉堆栈跟踪信息,并且完全错误。


这也是为什么如果我需要为日志记录而这样做,我会将其包装为内部异常的另一个原因。 - tvanfosson
为什么?只需抛出原始异常。记录日志不应更改或修改任何内容。 - GEOCHET
6
为了增加异常的语义,例如,在我的方法中,当我尝试插入一行具有重复主键值的对象时,会出现SqlException。我知道正在插入什么类型的对象以及哪个关键值。我可以编写更好的异常消息,同时保留所有信息。 - tvanfosson
这与日志记录无关。是一个不同的概念。 - GEOCHET

15

我想不出任何为了功能而这样做的理由。但是,当之前存在一些错误处理(通常是日志记录)并已被删除,开发人员删除了日志处理但没有重构代码以删除冗余的try/catch时,可能会出现这种情况。


5

这可能是在调试过程中留下的一些代码(您会设置一个断点来检查调试器中的异常)。如果我想要记录异常并将其传递到上层,我可能会做类似于这样的事情,虽然我可能会用另一个更有意义(对我的应用程序而言)的错误消息来包装这个异常。


4

我可以看到这种架构(设计模式)在处理事务时被使用。函数执行其工作,失败,然后catch块将事务完成到已知状态(通常是回滚),然后抛出用户定义的异常。

目前,将该代码重构为更合理的状态。


我也看到了完全相同的模式。 - StayOnTarget

1

不仅try/catch是无意义的,IF也是如此。整个函数可以简化为一行:

return successful
在这个时候,为什么还要麻烦呢?为什么不直接测试“成功”而不是调用函数呢? 好吧,没问题,这是一个Web方法,我想你需要这个函数只是为了给Ajax或其他人一个句柄。 (是的,我知道我的回答晚了7年。我只是在搜索完全无关的东西时偶然发现了这个。)

1

来自Microsoft的CodeAnalysis: (CA2200: Rethrow to preserve stack details)

"一旦抛出异常,它所携带的信息中包含了堆栈跟踪。堆栈跟踪是一个方法调用层次结构列表,从抛出异常的方法开始,以捕获异常的方法结束。如果通过在throw语句中指定异常来重新抛出异常,则堆栈跟踪将从当前方法重新启动,并且在原始抛出异常的方法和当前方法之间的方法调用列表将丢失。为了保留原始的堆栈跟踪信息,请使用不指定异常的throw语句。"

  Sub CatchAndRethrowImplicitly()
     Try
        ThrowException()
     Catch e As ArithmeticException
        ' Satisfies the rule.
        Throw
     End Try
  End Sub

https://learn.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details?view=vs-2019


0

有很多非常好的理由这样做,特别是在全栈/N层环境中。

在类库中使用Try/Catch捕获代码异常并记录异常信息,然后将其抛回给调用应用程序,以便调用方可以按照自己的方式处理异常。

请记住,在全栈环境中,各个堆栈应该是独立的,不知道彼此的内部功能,同样,它们也不应该假设知道外部堆栈希望如何处理已捕获的异常。实时处理异常,然后将其传递给调用方是非常有帮助的。

并且始终使用"Throw"而不是"Throw ex"。Throw将重新抛出已处理的异常,保持其堆栈完整。


0

对于监视器而言,模式使得重新抛出错误非常可能,因为你需要一个 finally 来确保调用 Monitor.Exit。

https://msdn.microsoft.com/en-us/library/4tssbxcw(v=vs.110).aspx

Dim lockObj As New Object()

If Monitor.TryEnter(lockObj) Then
    Try
        ' The critical section.
    Catch
       throw
    Finally
       ' Ensure that the lock is released.
       Monitor.Exit(lockObj)
    End Try
End If

-9

如果您想捕获除其子类以外的异常,那么您可能需要这样做。

例如,

try {
    // Something stupid
}
catch(RuntimeException e) {
    throw e; //Handle it outside
}
catch (Exception e) {
    // I'm dead
}

2
但你仍然不想调用 "throw e"。相反,你想要使用 "throw"。调用 "throw e" 会删除到该点的堆栈信息,这是很危险的。 - Brian Genisio
2
错误,错误,错误。请不要告诉别人这个。 - GEOCHET
@Mike:不需要了,应该直接删除或投票淹没它。 - GEOCHET
在Java中,它不会销毁堆栈。它保留了原始抛出的所有信息。 - Dennis C
哎呀,我没有注意到这个问题上有“vb.net”标签。 - Dennis C
显示剩余5条评论

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