我突然意识到,我从来没有见过一个单个异常层次结构,其中创建子类但捕捉父类实际上是有用的(当然,除了必须派生的基本异常类Exception)。
异常层次结构真的有用吗,还是所有异常都应该派生自语言的基本异常类?
我突然意识到,我从来没有见过一个单个异常层次结构,其中创建子类但捕捉父类实际上是有用的(当然,除了必须派生的基本异常类Exception)。
异常层次结构真的有用吗,还是所有异常都应该派生自语言的基本异常类?
异常层次结构对于将相关的异常分组很有用,当您需要在不同的位置使用不同的粒度进行捕获时。
将所有应用程序异常放在一个地方是最常见的用例。这允许您在适当的时候捕获更具体的异常,但仍然可以随时捕获MyAppException来捕获来自应用程序的所有错误。(在.NET中,ApplicationException类就是为此而设计的,但由于各种原因已被弃用。)
但是,您还可以按模块边界或任何其他有意义的方式将异常分组。使用FooModuleException处理来自Foo模块的异常,但在Foo内部特别处理FooModuleMustFrobnicate. 或任何等效情况。
我曾经在不同的时间使用过所有这些模式。
IOException
,而他们能得到的仅仅是一个 FileNotFoundException
。他们使用 IOException
是因为继承关系让他们认为可能会发生其他相关的事情,但实际上,IOException
的子类在出现的上下文方面相当独立。 - zneak我认为Java是这三种语言中唯一具有正确方法的层次结构,即:
Throwable
不在用户代码中使用Error
您永远不想捕获的东西 - 只需核心转储Exception
- 用于基本上始终想要捕获的所有内容(除非接口出错并在实际上不应该使用异常时)//
C++中的<stdexcept>
层次结构在区分logic_error
(基本上是断言)和runtime_error
(只是意外失败的东西)方面有正确的基本方法。(这些子类在catching时大多不太相关。)std::exception
派生,因此您无法利用由运行时错误和逻辑错误提供的区分。 (当然,任何代码都可以抛出任何他们想要的异常,因此很有可能有一堆应该是runtime_errors而实际上是logic_error导出类,反之亦然。)
但是根据我的经验,我从来都不想要“在不同的地方捕获”,因为这根本没有意义。异常层次结构适用于将相关异常分组在一起,当您需要在不同的地方具有不同的捕获粒度时。
Error
类异常之类的东西),要么就不想要捕获,此时异常层次不需要,因为根本没有什么可以捕获的。
执行打开和读取文件操作:
如果有我特别关注的异常类型,因为我可以做一些处理(“恢复”),那么这根本不应该被报告为异常,因为这是正常的控制流程。(如果错误是某种类型,我可以在本地执行某些操作。)
另一方面,如果我对错误无能为力,只能记录文件操作失败,那么任何异常捕获粒度都毫无用处。
我从未为整个应用程序创建过单一的异常层次结构,因为一般情况下异常可能会带来不同的语义,因此需要以不同的方式处理。
有些异常是关于系统故障的,另一些异常是关于错误的,还有一些异常是关于某些特殊情况的,这些情况可以在更高级别上优雅地恢复。
对于这些“业务逻辑”异常,异常层次结构可以帮助您在覆盖后代中的某些方法时不违反开闭原则。
考虑以下示例:
public abstract class Base
{
/// Perform some business-logic operation
/// and throw BaseFooException in certain circumstances
public abstract void Foo();
}
public class Derived1 : Base
{
/// Perform some business-logic operation
/// and throw DerivedFooException in certain circumstances.
/// But caller could catch only BaseFooException
public abstract void Foo() {}
}
class BaseFooException : Exception {}
class DerivedFooException : BaseFooException {}
异常层次结构在自定义框架或特定库中可能会有所帮助,但在一般情况下(在业务应用中),我认为创建一个深而广泛的异常层次结构不是一个好主意。