在某种程度上,您的问题取决于您的特定操作环境和语言(您暗示但从未透露),但在某种程度上,这些问题相当普遍。 (不幸的是,这也是一个存在很多分歧的领域,无论是在不同的操作环境之间还是在各个开发人员之间,有时在同一个项目中,所以我下面所说的可能需要根据您的上下文进行修改。)
从事异常工作 30 多年,我见过几乎所有异常机制和样式,包括一些非常原始的和一些过于“可爱”的。不过,有一些共同的原则:
首先,需要定义什么是异常。出于我们的目的,假设它是与当前执行的代码相关的异常情况的通知(而不是当前代码未使用的 I/O 设备上的情况)。假设它是相对于当前正在执行的代码同步交付的,而不是延迟(可能除了几个机器周期之外),也没有(最初)发送到不相关的代码。
继续这个思路,我们假设当前正在执行的程序有一个(至少是概念上的)调用返回堆栈,表示调用从程序执行开始到异常点的嵌套顺序。由于是这种情况,并且由于异常与当前执行的代码“相关”(例如,被零除是由于当前过程中的除法运算),因此同样存在与每个过程的排序关系调用返回堆栈。也就是说,每个过程都“知道”该异常直接或间接地由它自己的执行和它调用的过程引起。
这说明了什么?好吧,通常最接近错误的过程(即,位于调用返回堆栈“顶部”的那个)最“知道”错误的原因是什么,并且最适合处理它。但有时(例如,“找不到文件”异常)最接近异常点执行的过程没有足够的知识来处理错误(因为,例如,它不知道调用者在哪里得到了不存在的文件的名称,或者文件的内容应该代表什么)。出于这个原因,如果当前过程不能处理错误,它应该(隐式或显式)将错误“重新通知”给调用过程(依此类推,直到调用返回堆栈,直到找到想要处理错误的过程)。大多数异常方案会自动执行此“重新发出信号”,但有些异常方案需要程序员在一定程度上对其进行管理。
最终,如果错误未得到处理,您将到达调用返回堆栈的底部。这是默认处理程序接管并决定做什么(终止程序,在代码中的某个“安全”重新启动点恢复等)的时候。默认处理程序也可能会尝试记录错误(以及调用返回堆栈内容),以便稍后对其进行调试。
因此(由于您的特定操作环境没有任何理由),处理异常的最佳方法是在最“了解”它们的含义的各个过程中。在某些情况下,过程可能只知道一般意义上的异常意味着什么(例如,出于某种原因,除以零意味着“无效权重”),因此它可能会使用新的程序定义的异常类型重新发出信号,期望它的调用者(或其调用者的调用者......)将“知道”“无效重量”的含义以及如何处理它)。
许多人认为异常应该只用于处理“灾难性”错误,如果不是完全的程序终止,那么这些错误至少会给用户带来令人讨厌的错误消息,但这种观点是完全错误的。使用异常来管理无效的用户输入是非常好的,事实上,从“结构化编程”的角度来看,使用异常通常比使用返回码等更好。唯一需要注意的是,在某些环境中异常会非常缓慢,并且不应该在它们可能频繁发生的地方使用。但大多数较新的环境更加“开明”,在它们中可以使用异常而不用担心性能。
使用异常时确实需要稍微改变编程风格。最重要的是,必须注意构建代码,以便异常处理范围的开始/结束标记对应于与该可能异常相关的概念操作的开始和结束,并且必须确保任何“退出”代码发生异常时撤消部分完成的操作所需的操作适当地连接到该异常范围(例如,使用“finally”子句,或者,如果语言不支持,作为一个单独的子例程,可以从异常范围的异常处理程序)。但是一旦你开始理解这类事情,错误恢复就会变得非常非常简单。
在一般情况下,不处理无法处理的异常并让默认处理程序处理它们是完全可以的。然而,在某些情况下,可能需要关闭文件或释放资源。有时这可以通过默认处理程序完成,但在其他情况下,最好使用单独的异常处理程序(通常设置为捕获“任何”异常)来处理,这些处理程序只需执行所需的清理操作,然后重新发出原始异常的信号。异常允许您在代码中首先分配资源的位置附近执行此操作,因此默认处理程序不需要包含所有已分配资源的大列表并知道如何全部释放它们.这种技术可以显着减少由于资源未释放而导致的错误。
“展开堆栈(跟踪)”可能有几种不同的含义,但一般来说,当过程 A 中发生异常但通过调用返回堆栈返回到 A 的调用者 B 再到 B 的调用者 C 时,比如说,并且 C 的异常处理程序“处理”异常(不会将其重新发送给 C 的调用者),需要将调用返回堆栈恢复到 C 执行时的状态,以便 C 可以继续“正常” "(或多或少)执行。为此,堆栈上 A 和 B 的条目将(至少在概念上)“弹出”或“展开”。有时(取决于系统/语言)这种“展开”发生在未处理异常的“涟漪”发生时,但有时直到异常被标记为“已处理”才完成。通常你不需要关心这个,但一些系统或一些特殊情况可能需要了解它。
还需要注意的是,大多数系统都会在发生异常时以某种方式使调用返回堆栈的内容可用于记录为调试信息。这非常有用,尤其是在调试“远程”错误时,值得了解该机制在您的系统上是如何工作的。