我为我的应用程序不期望的每个条件创建了异常。UserNameNotValidException, PasswordNotCorrectException等。
然而,我被告知我不应该为这些条件创造例外。在我的UML中,那些是主要流程的异常,那么为什么它不应该是异常呢?
是否有创建异常的指导或最佳实践?
我为我的应用程序不期望的每个条件创建了异常。UserNameNotValidException, PasswordNotCorrectException等。
然而,我被告知我不应该为这些条件创造例外。在我的UML中,那些是主要流程的异常,那么为什么它不应该是异常呢?
是否有创建异常的指导或最佳实践?
当前回答
“PasswordNotCorrectException”不是一个使用异常的好例子。用户输入错误的密码是意料之中的,所以在我看来,这几乎不是个例外。您甚至可能从中恢复,显示一个漂亮的错误消息,因此这只是一个有效性检查。
未处理的异常将最终停止执行——这是好事。如果返回false、null或错误代码,则必须自己处理程序的状态。如果您忘记检查某个地方的条件,您的程序可能会继续使用错误的数据运行,并且您可能很难弄清楚发生了什么以及在哪里发生了什么。
当然,空的catch语句也可能导致同样的问题,但至少发现这些语句更容易,而且不需要理解逻辑。
所以根据经验:
在您不想要或无法从错误中恢复的地方使用它们。
其他回答
如果用户名无效或密码不正确,这不是一个例外。这些都是在正常操作流程中应该预料到的事情。异常不属于正常程序操作的一部分,而且相当罕见。
我不喜欢使用异常,因为仅仅通过查看调用就无法判断一个方法是否引发了异常。这就是为什么只有当你不能以一种体面的方式处理这种情况时才应该使用异常(比如“内存不足”或“电脑着火了”)。
一个经验法则是在您通常无法预测的情况下使用异常。例如数据库连接、磁盘上丢失的文件等。对于您可以预测的场景,例如用户试图使用错误的密码登录,您应该使用返回布尔值的函数,并知道如何优雅地处理这种情况。您不希望仅仅因为有人输入了密码错误而抛出异常,从而突然结束执行。
以下是我的建议:
我不认为这总是一个抛出异常的好方法,因为它将花费更多的时间和内存来处理这样的异常。
在我看来,如果某些事情可以用“友好、礼貌”的方式处理(这意味着如果我们可以“通过使用if......或类似的东西来预测这样的错误),我们应该避免使用“异常”,而只是返回一个像“false”这样的标志,用一个外部参数值告诉他/她详细的原因。
举个例子,我们可以这样创建一个类:
public class ValueReturnWithInfo<T>
{
public T Value{get;private set;}
public string errorMsg{get;private set;}
public ValueReturnWithInfo(T value,string errmsg)
{
Value = value;
errMsg = errmsg;
}
}
我们可以使用这种“多值返回”类来代替错误,这似乎是处理异常问题的一种更好、更礼貌的方式。
但是,请注意,如果一些错误不能如此容易地用"if"......(例如FileIO异常)描述(这取决于您的编程经验),则必须抛出异常。
抛出异常会导致堆栈unwind,这对性能有一定影响(承认,现代托管环境在这方面有所改进)。仍然在嵌套的情况下反复抛出和捕获异常是一个坏主意。
可能比这更重要的是,例外是针对特殊情况的。它们不应该用于普通的控制流,因为这会损害代码的可读性。
The simple answer is, whenever an operation is impossible (because of either application OR because it would violate business logic). If a method is invoked and it impossible to do what the method was written to do, throw an Exception. A good example is that constructors always throw ArgumentExceptions if an instance cannot be created using the supplied parameters. Another example is InvalidOperationException, which is thrown when an operation cannot be performed because of the state of another member or members of the class.
在您的情况下,如果调用Login(用户名,密码)这样的方法,如果用户名无效,抛出UserNameNotValidException或PasswordNotCorrectException(密码不正确)确实是正确的。用户不能使用提供的参数登录(即,这是不可能的,因为它将违反身份验证),因此抛出异常。尽管我可能从ArgumentException继承了两个异常。
话虽如此,如果因为登录失败可能很常见而不希望抛出异常,一种策略是创建一个方法,该方法返回表示不同失败的类型。这里有一个例子:
{ // 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))
{ }
}
Most developers are taught to avoid Exceptions because of the overhead caused by throwing them. It's great to be resource-conscious, but usually not at the expense of your application design. That is probably the reason you were told not to throw your two Exceptions. Whether to use Exceptions or not usually boils down to how frequently the Exception will occur. If it's a fairly common or an fairly expectable result, this is when most developers will avoid Exceptions and instead create another method to indicate failure, because of the supposed consumption of resources.
下面是一个使用Try()模式避免在类似刚刚描述的场景中使用exception的例子:
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;
}
}