我为我的应用程序不期望的每个条件创建了异常。UserNameNotValidException, PasswordNotCorrectException等。

然而,我被告知我不应该为这些条件创造例外。在我的UML中,那些是主要流程的异常,那么为什么它不应该是异常呢?

是否有创建异常的指导或最佳实践?


当前回答

避免抛出异常的主要原因是抛出异常涉及大量开销。

下面这篇文章指出的一件事是,例外是针对异常条件和错误的。

错误的用户名不一定是程序错误,而是用户错误……

下面是关于。net中的异常的一个不错的起点: http://msdn.microsoft.com/en-us/library/ms229030 (VS.80) . aspx

其他回答

我想说,对于什么时候使用异常并没有硬性规定。然而,使用或不使用它们有很好的理由:

使用异常的原因:

The code flow for the common case is clearer Can return complex error information as an object (although this can also be achieved using error "out" parameter passed by reference) Languages generally provide some facility for managing tidy cleanup in the event of the exception (try/finally in Java, using in C#, RAII in C++) In the event no exception is thrown, execution can sometimes be faster than checking return codes In Java, checked exceptions must be declared or caught (although this can be a reason against)

不使用异常的原因:

Sometimes it's overkill if the error handling is simple If exceptions are not documented or declared, they may be uncaught by calling code, which may be worse than if the the calling code just ignored a return code (application exit vs silent failure - which is worse may depend on the scenario) In C++, code that uses exceptions must be exception safe (even if you don't throw or catch them, but call a throwing function indirectly) In C++, it is hard to tell when a function might throw, therefore you must be paranoid about exception safety if you use them Throwing and catching exceptions is generally significantly more expensive compared to checking a return flag

一般来说,我更倾向于在Java中使用异常,而不是在c++或c#中,因为我认为异常,无论是否声明,都是函数正式接口的基本组成部分,因为更改异常保证可能会破坏调用代码。在Java IMO中使用它们的最大优势是,您知道调用者必须处理异常,这提高了正确行为的机会。

正因为如此,在任何语言中,我总是从一个公共类派生一层代码或API中的所有异常,这样调用的代码就总能保证捕获所有异常。另外,我认为在编写API或库时抛出特定于实现的异常类是不好的(即从较低的层包装异常,以便调用者接收到的异常在您的接口上下文中是可以理解的)。

注意,Java区分了一般异常和运行时异常,因为后者不需要声明。我只会使用运行时异常类,当您知道错误是由程序中的错误导致的。

我个人的指导方针是:当发现当前代码块的基本假设为假时抛出异常。

例1:假设我有一个函数,它应该检查任意类,如果该类继承自List<>,则返回true。这个函数问一个问题:“这个对象是List的后代吗?”这个函数永远不会抛出异常,因为它的操作中没有灰色地带——每个单独的类要么继承了List<>,要么继承了List<>,所以答案总是“是”或“否”。

Example 2: say I have another function which examines a List<> and returns true if its length is more than 50, and false if the length is less. This function asks the question, "Does this list have more than 50 items?" But this question makes an assumption - it assumes that the object it is given is a list. If I hand it a NULL, then that assumption is false. In that case, if the function returns either true or false, then it is breaking its own rules. The function cannot return anything and claim that it answered the question correctly. So it doesn't return - it throws an exception.

这与“负载问题”逻辑谬误相当。每个函数都问一个问题。如果给出的输入使该问题成为谬误,则抛出异常。对于返回void的函数,这条线很难画出来,但底线是:如果函数对其输入的假设违反了,它应该抛出异常,而不是正常返回。

这个等式的另一方面是:如果你发现你的函数经常抛出异常,那么你可能需要改进它们的假设。

抛出异常会导致堆栈unwind,这对性能有一定影响(承认,现代托管环境在这方面有所改进)。仍然在嵌套的情况下反复抛出和捕获异常是一个坏主意。

可能比这更重要的是,例外是针对特殊情况的。它们不应该用于普通的控制流,因为这会损害代码的可读性。

异常是一种代价高昂的效果,例如,如果您有一个用户提供了无效的密码,那么通常更好的方法是返回一个失败标志,或其他一些无效的指示。

这是由于异常处理的方式,真正的错误输入和唯一的关键停止项应该是异常,而不是失败的登录信息。

一般来说,你想要为应用程序中可能发生的任何异常抛出一个"异常"

在您的示例中,这两个异常看起来都是通过密码/用户名验证调用的。在这种情况下,有人会输入错误的用户名/密码并不是什么例外。

它们是UML主要流程的“例外”,但在处理过程中是更多的“分支”。

如果您试图访问您的passwd文件或数据库,但无法访问,这将是一个异常情况,并需要抛出异常。