Die einfache Antwort lautet, wann immer eine Operation unmöglich ist (entweder aufgrund einer Anwendung ODER aufgrund einer Verletzung der Geschäftslogik). Wenn eine Methode aufgerufen wird und es nicht möglich ist, das zu tun, wofür die Methode geschrieben wurde, lösen Sie eine Ausnahme aus. Ein gutes Beispiel ist, dass Konstruktoren immer ArgumentExceptions auslösen, wenn eine Instanz nicht mit den angegebenen Parametern erstellt werden kann. Ein weiteres Beispiel ist InvalidOperationException, die ausgelöst wird, wenn eine Operation aufgrund des Status eines anderen Mitglieds oder von Mitgliedern der Klasse nicht ausgeführt werden kann.
Wenn in Ihrem Fall eine Methode wie Login (Benutzername, Passwort) aufgerufen wird und der Benutzername nicht gültig ist, ist es in der Tat richtig, eine UserNameNotValidException oder eine PasswordNotCorrectException auszulösen, wenn das Passwort falsch ist. Der Benutzer kann nicht mit den angegebenen Parametern angemeldet werden (dh es ist unmöglich, da dies die Authentifizierung verletzen würde). Setzen Sie daher eine Ausnahme. Obwohl ich möglicherweise Ihre beiden Ausnahmen von ArgumentException erben lassen habe.
Wenn Sie jedoch KEINE Ausnahme auslösen möchten, weil ein Anmeldefehler sehr häufig sein kann, besteht eine Strategie darin, stattdessen eine Methode zu erstellen, die Typen zurückgibt, die unterschiedliche Fehler darstellen. Hier ist ein Beispiel:
{ // 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))
{ }
}
Den meisten Entwicklern wird beigebracht, Ausnahmen aufgrund des durch das Auslösen verursachten Overheads zu vermeiden. Es ist großartig, ressourcenbewusst zu sein, aber normalerweise nicht auf Kosten Ihres Anwendungsdesigns. Das ist wahrscheinlich der Grund, warum Ihnen gesagt wurde, dass Sie Ihre beiden Ausnahmen nicht werfen sollen. Ob Ausnahmen verwendet werden sollen oder nicht, hängt normalerweise davon ab, wie häufig die Ausnahme auftritt. Wenn es sich um ein ziemlich häufiges oder zu erwartendes Ergebnis handelt, vermeiden die meisten Entwickler Ausnahmen und erstellen stattdessen eine andere Methode, um auf einen Fehler hinzuweisen, da angeblich Ressourcen verbraucht werden.
Hier ist ein Beispiel für die Vermeidung der Verwendung von Ausnahmen in einem soeben beschriebenen Szenario mithilfe des Try () -Musters:
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;
}
}