単純な答えは、操作が不可能なときはいつでもです(アプリケーションまたはビジネスロジックに違反するため)。メソッドが呼び出され、そのメソッドが実行するように記述されていることを実行できない場合は、例外をスローします。良い例は、提供されたパラメーターを使用してインスタンスを作成できない場合、コンストラクターが常にArgumentExceptionsをスローすることです。別の例は、InvalidOperationExceptionです。これは、クラスの別のメンバーの状態が原因で操作を実行できない場合にスローされます。
あなたのケースでは、Login(username、password)のようなメソッドが呼び出された場合、ユーザー名が有効でない場合、UserNameNotValidExceptionをスローすることは確かに正しく、パスワードが正しくない場合はPasswordNotCorrectExceptionをスローします。ユーザーは提供されたパラメーターを使用してログインできない(つまり、認証に違反するため不可能です)ため、例外をスローします。2つの例外が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))
{ }
}
ほとんどの開発者は、例外をスローすることによって生じるオーバーヘッドのため、例外を回避するように教えられています。リソースを意識することは素晴らしいことですが、通常はアプリケーション設計を犠牲にすることはありません。これがおそらく、2つの例外をスローしないように言われた理由です。例外を使用するかどうかは、通常、例外が発生する頻度によって決まります。それがかなり一般的またはかなり期待できる結果である場合、これはほとんどの開発者が例外を回避し、代わりにリソースの想定消費のために失敗を示す別のメソッドを作成するときです。
Try()パターンを使用して、今説明したようなシナリオで例外の使用を回避する例を次に示します。
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;
}
}