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");