“编程错误”例外-我的方法是否正确?


9

我目前正在尝试改善对异常的使用,发现了在表示编程错误的异常(例如,有人将null作为参数传递,或在对象被处置后调用方法)与在编程中指示失败的异常之间的重要区别。不是调用者的错误的操作(例如,I / O异常)。

这两种例外应如何区别对待?您是否认为需要明确记录错误异常,还是足以记录相关的前提条件?并且您是否可以忽略前提条件或错误异常的文档(如果它很明显)(例如,ObjectDisposedException在处置对象上调用方法时)


我通常区分另一类例外:业务例外。这些是用户对细节感兴趣的错误条件。我通常会检查那些异常。
beluchin

Answers:


2

我认为您在正确的轨道上。抛出,捕获或记录所有可能抛出的异常都没有多大意义。有时产品严格度要求更高程度的例外采用和记录(例如,系统的某些安全关键方面)。

使用合同概念来确定特别是下游呼叫者(例如,类似于公共或受保护成员的任何呼叫者)的前提条件(例如,类似公共或受保护成员的条件)的防御性更高的策略通常会更有效,更灵活。这不仅适用于实现,而且适用于文档。如果开发人员知道预期的结果,那么他们更有可能遵守规则,而不太可能混淆或滥用您编写的代码。

应该记录的一些常见事物包括空参数的情况。通常,使用它们会导致某种结果,使结果达到通常无法预期的程度,但由于各种原因(有时出于灵活性)而被允许和使用。作为具有允许空值或其他特殊的非有理值(例如负时间或负数量)的参数的成员的消费者,我希望可以看到它们并加以说明。

对于非null参数,作为公共或受保护成员的使用者,我想知道不允许null。我想知道给定上下文中的有效值范围是多少。我想知道使用超出正常范围但在其他调用上下文中有效的值的后果(例如,该类型的值通常对任何操作均有效,但不适用于此操作-就像布尔参数那样)不要期望false为有效值。

就平台或其他众所周知的界面而言,我认为您不必费力地记录下来。但是,由于您作为开发人员有机会从任何平台指南中更改实现,因此请注意该指南遵循的价值。

特定于IDisposable,此接口的实现通常提供了一种替代方法,该方法比显式处理过程更可取。在这些情况下,请突出显示首选方法,并注意不要采用显式处理。


我知道需要记录允许使用哪些值,但是如果您看到一个函数performReadCommand(ICommand cmd, int replySizeBytes)-您是否希望cmd接受null或replySizeBytes为负值?只有在实际允许这些值的情况下,才需要IMO文档,并且您可能应该解决0对于ReplySizeBytes是否有效。
Medo42

根据实现的不同,它们都可能是“有效的”。您是我,甚至这里的每个人都不会期望null或负值适合该签名,但是可以。考虑一个环境,其中阅读器是阻塞的持久性进程,并且当cmd为null时,可能意味着不更改阅读器使用的命令,而继续使用下一个命令进行下一次读取。理想情况下,将定义和使用将cmd省略为参数的更合适的方法签名,但可能不是。
JustinC

2

这是我自己的想法。请注意,我不太确定这是最好的方法,这就是为什么我首先提出这个问题的原因。

据我了解,立即调用者实际处理编程错误异常没有什么意义,他应该确保满足先决条件。只有位于任务边界的“外部”异常处理程序才能捕获它们,因此,如果任务失败,它们可以使系统保持运行。

为了确保客户端代码可以干净地捕获“失败”异常而不会错误地捕获错误异常,我现在为所有失败异常创建自己的异常类,并在引发它们的方法中记录它们。我会让他们在Java中检查异常。

直到最近,我还尝试记录一个方法可能引发的所有异常,但是有时会创建一个不希望看到的列表,该列表需要在调用链的每个方法中记录下来,直到您可以证明不会发生错误为止。相反,我现在在摘要/参数描述中记录前提条件,甚至不提及如果不满足前提条件会发生什么。这样的想法是,人们无论如何都不应试图明确地捕获这些异常,因此无需记录其类型。

为了记录前提条件,陈述显而易见的条件只会造成不必要的混乱-如果将null传递给方法没有明显的意义,则调用者无论如何传递null都必须期望异常,即使未记录也是如此。对于ObjectDisposedException而言,情况也是如此-该接口的使用是如此广泛,以至于调用Dispose的人将意识到确保没有人继续使用该对象的责任。


1

合理的经验法则:

  1. 捕获任何可以修复的异常。
  2. 捕获,评论并抛出您无法解决但可以说出有用信息的任何异常。
  3. 不要捕获任何无法处理或无法比默认处理更好地诊断的异常。

您可能需要一个顶级的非致命异常处理程序来捕获无法在下面进行处理的任何内容,以防止应用程序崩溃,但这将很大程度上取决于您的特定应用程序。例如,iOS应用程序更喜欢捕获尽可能多的陷阱。如果命令行应用程序几乎没有捕获任何异常,则可能完全正常。


0

甚至Java也不“记录”所有异常。尽管要求throwthrows子句中提及每个n个异常,但是任何代码行都可以抛出a RuntimeException而不需要在方法签名中声明它。


但是,这与流行的建议背道而驰-特别是,有效Java,第二版,条款62是“记录每种方法引发的所有异常”。实际上,Bloch承认未经检查的异常通常是由于编程错误引起的,但也应仔细记录它们,因为这是记录方法前提的“最佳方法”。我不确定我是否同意。如果我记录了这些执行,则使它们成为接口协定的一部分,并且如果我的实现发生更改,可能必须添加代码以使它们保持不变。
Medo42

1
对于每位专家,都有一位反专家。布鲁斯·埃克尔(Bruce Eckel)是著名的Java思维(Thinking in Java)的作者,曾经在亲检查例外阵营中,并且改变了立场。Eckels与 C#的创建者Anders Hejlsberg之间进行很好的讨论,其中没有检查异常。
罗斯·帕特森

0

标准的方法是使用Java方法。编程错误应该是未经检查的异常,并且不应被捕获以确保快速失败。合同错误是检查过的异常,应由客户适当处理。


2
您是否将(例如)计算出的(或错误值)NULL视为编程错误或合同错误?我同意您的意见,但界限不那么清晰:)
Andrew

如果null是根据传入的参数(例如null参数或某些无效参数)计算得出的,则表明合同错误。如果由于一段错误的代码而计算出null,则是编程错误。如果应该计算null,则不是错误。在这个例子中,我并没有发现任何歧义。
J.Ashworth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.