Ответ прост: всякий раз, когда операция невозможна (либо из-за приложения, либо из-за нарушения бизнес-логики). Если метод вызывается и невозможно сделать то, для чего был написан метод, выдается исключение. Хорошим примером является то, что конструкторы всегда выдают ArgumentExceptions, если экземпляр не может быть создан с использованием предоставленных параметров. Другой пример - InvalidOperationException, который генерируется, когда операция не может быть выполнена из-за состояния другого члена или членов класса.
В вашем случае, если вызывается такой метод, как 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))
{ }
}
Большинство разработчиков учат избегать исключений из-за накладных расходов, вызванных их выбросом. Здорово быть внимательным к ресурсам, но обычно не в ущерб дизайну вашего приложения. Это, вероятно, причина, по которой вам сказали не бросать ваши два исключения. Использовать ли исключения или нет обычно сводится к тому, как часто будет происходить исключение. Если это довольно распространенный или вполне ожидаемый результат, то это когда большинство разработчиков избегают исключений и вместо этого создают другой метод для указания сбоя из-за предполагаемого потребления ресурсов.
Вот пример того, как избежать использования Исключений в сценарии, как только что описано, с помощью шаблона 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;
}
}