La respuesta simple es, siempre que una operación sea imposible (debido a cualquiera de las aplicaciones O porque violaría la lógica de negocios). Si se invoca un método y es imposible hacer para lo que fue escrito, lanzar una excepción. Un buen ejemplo es que los constructores siempre arrojan ArgumentExceptions si no se puede crear una instancia utilizando los parámetros proporcionados. Otro ejemplo es InvalidOperationException, que se produce cuando no se puede realizar una operación debido al estado de otro miembro o miembros de la clase.
En su caso, si se invoca un método como Iniciar sesión (nombre de usuario, contraseña), si el nombre de usuario no es válido, es correcto lanzar una UserNameNotValidException o PasswordNotCorrectException si la contraseña es incorrecta. El usuario no puede iniciar sesión con los parámetros proporcionados (es decir, es imposible porque violaría la autenticación), por lo que debe lanzar una excepción. Aunque podría tener sus dos Excepciones heredadas de ArgumentException.
Dicho esto, si NO desea lanzar una Excepción porque un error de inicio de sesión puede ser muy común, una estrategia es crear un método que devuelva tipos que representen errores diferentes. Aquí hay un ejemplo:
{ // 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))
{ }
}
A la mayoría de los desarrolladores se les enseña a evitar Excepciones debido a la sobrecarga causada por lanzarlos. Es genial ser consciente de los recursos, pero generalmente no a expensas del diseño de su aplicación. Esa es probablemente la razón por la que le dijeron que no lanzara sus dos Excepciones. Si usar Excepciones o no, generalmente se reduce a la frecuencia con que ocurrirá la Excepción. Si es un resultado bastante común o bastante esperado, es cuando la mayoría de los desarrolladores evitarán las Excepciones y, en su lugar, crearán otro método para indicar la falla, debido al supuesto consumo de recursos.
Aquí hay un ejemplo de cómo evitar usar Excepciones en un escenario como el que acabamos de describir, usando el patrón 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;
}
}