Exception 何时抛出异常?

Exception 何时抛出异常?,exception,language-agnostic,Exception,Language Agnostic,对于我的应用程序不期望的每个条件,我都会创建异常UserNameNotValidException,PasswordNotCorrectException等 然而,有人告诉我,我不应该为这些条件创建例外。在我的UML中,这些是主流程的异常,那么为什么它不应该是异常呢 创建异常的任何指导或最佳做法?异常的代价有些高,例如,如果您有一个用户提供了无效密码,通常最好传回故障标志或其他一些无效指示 这是由于异常的处理方式、真正的错误输入和唯一的关键停止项应该是异常,而不是失败的登录信息。因为它们是正常发

对于我的应用程序不期望的每个条件,我都会创建异常
UserNameNotValidException
PasswordNotCorrectException

然而,有人告诉我,我不应该为这些条件创建例外。在我的UML中,这些是主流程的异常,那么为什么它不应该是异常呢


创建异常的任何指导或最佳做法?

异常的代价有些高,例如,如果您有一个用户提供了无效密码,通常最好传回故障标志或其他一些无效指示


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

因为它们是正常发生的事情。异常不是控制流机制。用户经常会得到错误的密码,这不是一个例外情况。异常应该是非常罕见的,
userhasdiedatcuide
键入情况。

如果用户名无效或密码不正确,则不是异常。这些都是您在正常操作流程中应该期待的事情。异常不是正常程序操作的一部分,非常罕见


编辑:我不喜欢使用异常,因为您无法通过查看调用来判断方法是否引发异常。这就是为什么只有当你不能以适当的方式处理情况时(想想“内存不足”或“计算机着火了”)才应该使用异常的原因。

我认为只有当你无法摆脱当前状态时才应该抛出异常。例如,如果您正在分配内存,但没有可分配的内存。在您提到的情况下,您可以清楚地从这些状态中恢复,并相应地将错误代码返回给调用方


您将看到很多建议,包括在对这个问题的回答中,您应该只在“异常”情况下抛出异常。这看似合理,但却是有缺陷的建议,因为它用另一个主观问题(“什么是例外”)取代了一个问题(“我应该何时抛出例外”)。相反,请遵循Habor萨特(C++中的建议,在Andrei Alexandrescu中,也可以在他的书中使用Andrei Alexandrescu,C++编码标准):抛出异常,如果仅是

  • 未满足先决条件(这通常会导致以下情况之一 不可能)或
  • 替代方案将无法满足post条件或
  • 替代方案将无法保持不变
为什么这样更好?它不是用几个关于前置条件、后置条件和不变量的问题来代替这个问题吗?出于几个相关的原因,这样做更好

  • 前置条件、后置条件和不变量是我们程序(其内部API)的设计特征,而抛出的决定是一个实现细节。它迫使我们记住,我们必须单独考虑设计及其实现,而在实现方法时,我们的工作是产生满足设计约束的东西。
  • 它迫使我们从前置条件、后置条件和不变量的角度进行思考,这是方法调用方应该做出的唯一假设,并且被精确地表达出来,从而实现程序组件之间的松散耦合
  • 然后,这种松耦合允许我们在必要时重构实现
  • 后置条件和不变量是可测试的;它产生的代码可以很容易地进行单元测试,因为post条件是我们的单元测试代码可以检查(断言)的谓词
  • 根据后条件进行思考自然会产生一个设计,该设计作为后条件获得成功,这是使用异常的自然风格。程序的正常(“快乐”)执行路径是线性排列的,所有错误处理代码都移动到
    catch
    子句中

一条经验法则是,在您通常无法预测的情况下使用异常。例如数据库连接、磁盘上缺少文件等。对于您可以预测的情况,即试图使用错误密码登录的用户,您应该使用返回布尔值的函数,并知道如何优雅地处理这种情况。您不希望仅仅因为某人输入了错误的密码就抛出异常而突然结束执行。

异常类与“普通”类类似。当新类“是”不同类型的对象、具有不同的字段和不同的操作时,您可以创建一个新类


根据经验,您应该尝试在异常数量和异常粒度之间取得平衡。如果您的方法引发超过4-5个不同的异常,您可能可以将其中一些合并为更“一般”的异常(例如,在您的情况下为“AuthenticationFailedException”),并使用异常消息详细说明出错的原因。除非您的代码以不同的方式处理它们,否则您不需要创建许多异常类。如果是这样的话,您应该返回一个带有发生错误的枚举。这样更干净。

避免引发异常的主要原因是引发异常会带来很多开销

下面这篇文章指出的一点是,例外是针对例外情况和错误的

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

以下是.NET中异常的一个不错的起点:

通常,您希望对应用程序中可能发生的任何“异常”事件抛出异常

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

<
            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If
{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}
public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
exchange_command("open tempfile"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("close tempfile"); exchange_command("copy tempfile to realfile");