21

在任何情况下,throw可以避免的错误是个好主意吗?

我正在特别考虑DivideByZeroExceptionArgumentNullException

例如:

double numerator = 10;
double denominator = getDenominator();

if( denominator == 0 ){
   throw new DivideByZeroException("You can't divide by Zero!");
}

是否有任何理由引发这样的错误?

注意:我不是在谈论捕获这些错误,而是特别是要知道是否有充分的理由抛出它们。

只是重申

我知道在我给你的例子中处理错误可能会更好。也许这个问题应该改写。这些错误之一是否有任何原因throw而不是在位置处理它。

4

16 回答 16

20

假设您编写了一个库来处理不适合 Int64 的非常大的整数,那么您可能想要为您编写的除法算法抛出 DivideByZeroException。

于 2010-04-08T15:57:49.130 回答
14

.NET 运行时已经非常擅长抛出这些异常。它们还高度描述了错误,您不能在异常消息中添加任何好的评论。“你不能除以零”没有任何价值。

但是,您可以通过筛选客户端代码传递的值来避免许多此类异常。如果客户端代码传递了一个空值 ArgumentException,则当它传递可能会导致 DivideByZero 绊倒的东西时,您将需要带有参数名称的 ArgumentNullException。

于 2010-04-08T15:56:57.913 回答
5

这样做绝对是有理由的,并且与大多数抛出异常的问题一样,您必须将自己视为团队中的开发人员。您正在编写的内容可能是另一个系统的一部分,也可能是库或组件。

如果您编写了一个数学库,并且有一个调用函数Divide(int a, int b)将它们分开,如果其他人想使用您的方法并传入零,您将要抛出异常,以便他们作为开发人员知道他们搞砸了.

如果不抛出异常,就会在代码中引入错误。如果我使用您的除法,并将值 10 和 0 放入其中,这会产生 10/0 并因此产生异常,那么唯一正确的答案是错误。如果您决定更改值以便不发生错误或返回 0,这不是我所期望的 - 我假设我的部门运行良好,并且从未注意到问题。我很可能会拿那个 0 并在其他计算中使用它,但不知道这是错误的答案,因为 10/0 不是 0。

于 2010-04-08T16:04:39.063 回答
4

您应该只使用异常来处理异常情况。

在这种情况下,看起来零是无效的用户输入。您应该检查无效的用户输入并相应地处理它,然后才能达到异常合适的程度。

只要您正确处理输入,就没有理由DivideByZeroException发生 a 。如果您DivideByZeroException在验证输入后收到一个 even...那么您知道您有一个真正的问题(在这种情况下,异常是合适的)。

于 2010-04-08T15:53:38.647 回答
3

如果您谈论的是直接与 UI 交互的代码,那么不是。我想不出任何你想要的理由。

但是,如果您正在构建其他开发人员使用的类/对象,并且他们传入的数据显然很愚蠢,那么在验证期间抛出适当的错误。

于 2010-04-08T15:55:38.773 回答
2

如果您正在编写 BigInteger 库,那么您绝对应该抛出一个适当的DivideByZero异常。如果你正在编写一个购物车库,那么……嗯,可能不是。

目前,我想不出抛出NullReferenceException. 如果您要创建一个 API,其文档显示“第一个参数HummingBirdFeeder.OpenSugarWaterDispenser(Dispenser dispenser, int flowRate)是您希望打开的糖水分配器”。如果有人随后出现并将 null 值传递给此方法,那么您绝对应该抛出一个ArgumentNullException.

但是,从您的 API 中释放aNullReferenceException只是一种懒惰,因为这是错误的,并且会泄露您的一些内部结构。

编辑添加:

既然您将问题更改为引用 anArgumentNullException那么答案很简单:是的,在这些情况下您绝对应该抛出这种异常:

  • 您正在编写一个公共方法。
  • 为参数获取空值在某种程度上是不合适的(即,如果没有该特定参数,该方法将毫无意义)。
  • 该方法的文档表明不允许使用空值。

在这种情况下,您的方法应该做的第一件事是检查空值并在违反条件时抛出异常。

如果您正在编写私有或内部方法,那么在大多数情况下,您不需要在发布版本的运行时验证参数。您可以确保您自己的代码正确调用您自己的代码。有助于创建保证的一件事是通过添加断言来验证调试构建中的参数:

Debug.Assert(dispenser != null);

通过这种方式,您可以验证代码是否正确运行并尽早发现任何错误,而不会因一堆无用的冗余检查而减慢已发布代码的速度。

于 2010-04-08T16:08:01.180 回答
1

在您自己的代码中,我怀疑会有很多用处。但是,如果您正在编写一个将被其他人使用的库,那么您应该使用异常作为将错误传达回使用您的库的代码的方法。

“在框架中,异常用于硬错误和逻辑错误。起初,很难将异常处理作为报告所有功能故障的手段。但是,重要的是设计框架的所有公共方法以通过抛出异常报告方法失败。”

Krzysztof Cwalina,设计可重用框架

于 2010-04-08T16:06:52.567 回答
1

这里有点误导的标题,它不是关于抛出(特殊)DivideByZeroException,而是问题:

我应该在验证用户输入时使用异常吗?

这可能以前被问过。我的看法:不,只需使用让您使用错误代码或布尔值处理此问题的流程。

然而,当在应用程序的更深层验证“数据”时,抛出异常通常是正确的做法。

于 2010-04-08T16:08:44.347 回答
1

如果您正在编写某种形式的数字类,并且可以预期调用您的类的代码已经执行了验证,那么我可以看到抛出 DivisionByZeroException 可能很有用,但在几乎任何其他情况下,最好在操作之前检查并抛出和 ArgumentException 或 ArgumentOutOfRange 异常(视情况而定)。

NullReferenceException 是框架使用的保留异常之一,您永远不应通过公共 API 抛出该异常,以及 IndexOutOfRange、AccessViolationException 和 OutOfMemoryException。请参阅框架设计指南书或代码分析警告说明

于 2010-04-08T16:13:29.873 回答
0

没有我能想到的。我只会针对它进行编程。异常可能很昂贵,因此如果您可以针对它进行防御性编程,请这样做而不是抛出异常。

至少这是我的高级开发人员在很多个月前尝试捕获 DivideByZeroException 时告诉我的。

于 2010-04-08T15:51:42.467 回答
0

如果您依赖框架来完成工作(.NET 或其他),那么您应该让框架抛出异常

这将使您的方法的使用者更容易处理以标准方式抛出的异常。

在除以零的情况下,您只是重新实现与框架中相同的逻辑来引发异常。

于 2010-04-08T17:04:56.677 回答
0

Never(用户永远不会发送 0 作为分母)和always(用户将始终提供适当的参数值)是包含在代码中的危险想法。我们中的一小部分人单独编程,代码永远不会被其他开发人员修改或调用。所以,我们需要编写防御性代码。在除以零的情况下,我不应该决定适当的结果应该是什么,这取决于调用者。抛出异常似乎是解决问题的合理方式。虽然涉及费用,但我添加到代码中以避免引发异常的任何基础设施也会增加费用。

于 2010-04-08T16:26:31.113 回答
0

在开始处理数据之前,在函数顶部进行这些检查是一个很好的做法。而不是在方法中间抛出它们(或任何其他方法抛出这些异常)。

于 2010-04-08T15:54:52.363 回答
0

在理论上(和实践也是),识别和避免/消除错误案例比将它们视为例外更好。但是,并非总是可以通过代码测试每个案例或路径 - 也不可能在每个案例中都控制您的输入。

良好设计的原则是让您的程序识别并优雅地处理异常情况。此外,向您的用户(最终是您自己)提供足够的信息来诊断错误是一个好主意——尤其是那些可能在您的代码部署之后发生的错误。因此,有时抛出异常是有意义的,例如说明它是否会使您的应用程序代码更健壮且更易于诊断

于 2010-04-08T15:59:57.440 回答
0

考虑到对于双精度数,除以零实际上会产生一个值:double.Infinity、double.NegativeInfinity 或 double.NaN,具体取决于分子是正数、负数还是零。

可能这些值已经适合您的情况,如果不是,您可能希望在执行除法之前验证输入并采取相应措施。

如果您正在编写一个 API(例如在库上)并且想要提供一个不允许除以零的协定(比如它公开了执行某种除法的方法),那么您可能想要抛出这个异常。不过,通常情况下,我会将这种类型的异常保留为程序错误的指示器。

于 2010-04-08T16:08:45.390 回答
-1

从资源的角度来看,抛出异常是一件昂贵的事情。您已经阻止了异常的发生,那么为什么要抛出异常呢?如果您必须告诉用户,请使用一些消息传递机制。如果您需要自己了解,请记录下来。

于 2010-04-08T15:53:09.133 回答