抛出异常是否具有不仅仅是构造异常的副作用

本文关键字:异常 副作用 不仅仅是 是否 抛出异常 | 更新日期: 2023-09-27 17:56:22

我脑子里有抛出异常有副作用(比如收集堆栈信息),但我意识到它可能不会。以下两个版本的GetException之间有区别吗?

public Exception GetException() {
    try {
        throw new Exception("Bummer");
    }
    catch (Exception e) {
        return e;
    }
}
public Exception GetException() {
    return new Exception("Bummer");
}

抛出异常是否具有不仅仅是构造异常的副作用

引发异常时,堆栈跟踪将"插入"到异常中(这是您可以通过 StackTrace 属性获取的内容)。所以是的,抛出异常对象有一个副作用。

重新抛出异常的问题(即使

throw;

是堆栈跟踪被破坏(甚至覆盖,如果您使用 throw ex;))

请注意,throw; throw ex; 更好,因为第一个只会破坏一点行号,而后者将完全重置堆栈跟踪。

有些人不信任...查看 https://dotnetfiddle.net/RXicN9 并检查行号。他们是不同的。

一个更完整的示例,显示只有具有两个try... catch...的方法的行号被篡改: https://dotnetfiddle.net/jJyYWB

有关说明,请参阅通过重新抛出的错误堆栈跟踪。请注意,这是一种特殊情况,仅当在同一方法中引发两次异常时才会发生。

是的,两者是不同的。throw将修改 Exception 对象中的堆栈跟踪信息 - 因此您的第二个示例仍将生成异常,但没有堆栈跟踪信息。

Exception派生类是与其他类一样的类 - 所有的"魔术"都发生在throw关键字上。

这也是为什么通过执行throw ex;来重新抛出异常是一个坏主意的原因 - 您要么想使用throw;(尽管以Xanatos指出的相同方法小心重新抛出的问题),要么想要包装内部异常(即。 throw new MyException(ex) )。在这两种情况下,仍然有一些小的更改可能会使调试复杂化,但如果你做好了充分的准备,它会有很大帮助。

这种行为实际上非常有用。首先,这意味着您可以使用帮助程序方法来构造要引发的异常(这在 .NET 代码本身中随处可见)。第二,它允许运行时实际抛出StackOverflowExceptionOutOfMemoryException等异常 - 两者都在应用程序启动时创建其实例,当问题发生时,它们只会抛出 - 当然,new会失败。