Jawaban sederhananya adalah, setiap kali operasi tidak mungkin (karena salah satu aplikasi ATAU karena akan melanggar logika bisnis). Jika suatu metode dipanggil dan tidak mungkin untuk melakukan apa metode itu ditulis untuk dilakukan, buanglah Pengecualian. Contoh yang baik adalah konstruktor selalu membuang ArgumentExceptions jika instance tidak dapat dibuat menggunakan parameter yang disediakan. Contoh lain adalah InvalidOperationException, yang dilemparkan ketika operasi tidak dapat dilakukan karena keadaan anggota lain atau anggota kelas.
Dalam kasus Anda, jika metode seperti Login (nama pengguna, kata sandi) dipanggil, jika nama pengguna tidak valid, memang benar untuk melempar UserNameNotValidException, atau PasswordNotCorrectException jika kata sandi salah. Pengguna tidak dapat masuk menggunakan parameter yang disediakan (yaitu tidak mungkin karena akan melanggar otentikasi), jadi lemparkan Pengecualian. Meskipun saya mungkin memiliki dua Pengecualian Anda mewarisi dari ArgumentException.
Karena itu, jika Anda ingin TIDAK melemparkan Pengecualian karena kegagalan login mungkin sangat umum, salah satu strategi adalah membuat metode yang mengembalikan tipe yang mewakili kegagalan yang berbeda. Ini sebuah contoh:
{ // 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))
{ }
}
Sebagian besar pengembang diajarkan untuk menghindari Pengecualian karena overhead yang disebabkan oleh melempar mereka. Sangat bagus untuk sadar sumber daya, tetapi biasanya tidak mengorbankan desain aplikasi Anda. Mungkin itulah alasan Anda diminta untuk tidak membuang dua Pengecualian Anda. Apakah akan menggunakan Pengecualian atau tidak biasanya bermuara pada seberapa sering Pengecualian akan terjadi. Jika ini adalah hasil yang cukup umum atau cukup diharapkan, inilah saat sebagian besar pengembang akan menghindari Pengecualian dan sebagai gantinya membuat metode lain untuk menunjukkan kegagalan, karena dugaan konsumsi sumber daya.
Berikut adalah contoh menghindari menggunakan Pengecualian dalam skenario seperti yang baru saja dijelaskan, menggunakan pola Coba ():
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;
}
}