Pengecualian apa yang harus diberikan untuk parameter yang tidak valid atau tidak terduga di .NET?

163

Apa jenis pengecualian yang harus dilemparkan untuk parameter yang tidak valid atau tidak terduga di .NET? Kapan saya memilih satu dan bukan yang lain?

Mengikuti:

Pengecualian mana yang akan Anda gunakan jika Anda memiliki fungsi yang mengharapkan bilangan bulat sesuai dengan bulan dan Anda melewati '42'? Apakah ini termasuk dalam kategori "di luar jangkauan" walaupun itu bukan koleksi?

Bahkan Mien
sumber

Jawaban:

249

Saya ingin menggunakan: ArgumentException, ArgumentNullException, dan ArgumentOutOfRangeException.

Ada opsi lain juga, yang tidak terlalu fokus pada argumen itu sendiri, tetapi menilai panggilan secara keseluruhan:

  • InvalidOperationException- Argumennya mungkin OK, tetapi tidak dalam keadaan objek saat ini. Kredit jatuh ke STW (sebelumnya Yoooder). Pilih jawabannya juga.
  • NotSupportedException- Argumen yang disahkan valid, tetapi tidak didukung dalam implementasi ini. Bayangkan klien FTP, dan Anda memberikan perintah bahwa klien tidak mendukung.

Caranya adalah dengan melemparkan pengecualian yang paling baik mengungkapkan mengapa metode ini tidak dapat disebut apa adanya. Idealnya, pengecualian harus dirinci tentang apa yang salah, mengapa itu salah, dan bagaimana cara memperbaikinya.

Saya suka ketika pesan kesalahan menunjuk ke bantuan, dokumentasi, atau sumber daya lainnya. Misalnya, Microsoft melakukan langkah pertama yang baik dengan artikel KB mereka, misalnya "Mengapa saya menerima pesan kesalahan" Operasi dibatalkan "ketika saya mengunjungi halaman Web di Internet Explorer?" . Ketika Anda menemukan kesalahan, mereka mengarahkan Anda ke artikel KB di pesan kesalahan. Yang tidak mereka lakukan dengan baik adalah bahwa mereka tidak memberi tahu Anda, mengapa secara khusus itu gagal.

Terima kasih kepada STW (ex Yoooder) lagi untuk komentarnya.


Menanggapi tindak lanjut Anda, saya akan melempar ArgumentOutOfRangeException. Lihatlah apa yang dikatakan MSDN tentang pengecualian ini:

ArgumentOutOfRangeExceptiondilemparkan ketika metode dipanggil dan setidaknya salah satu argumen yang diteruskan ke metode ini bukan referensi nol ( Nothingdalam Visual Basic) dan tidak mengandung nilai yang valid.

Jadi, dalam hal ini, Anda memberikan nilai, tetapi itu bukan nilai yang valid, karena rentang Anda adalah 1–12. Namun, cara Anda mendokumentasikannya memperjelas, apa yang dilemparkan API Anda. Karena walaupun saya mungkin mengatakan ArgumentOutOfRangeException, pengembang lain mungkin mengatakan ArgumentException. Permudah dan dokumentasikan perilakunya.

JoshBerke
sumber
Psst! Saya setuju bahwa Anda menjawab pertanyaan spesifiknya dengan tepat, tetapi lihat jawaban saya di bawah ini untuk membantu melengkapi pengkodean defensif dan memvalidasi parameter ;-D
STW
1 tetapi mendokumentasikan pengecualian apa yang dilemparkan dan mengapa lebih penting daripada memilih yang 'benar'.
pipTheGeek
@ pipTheGeek - Saya pikir ini benar-benar poin yang bisa diperdebatkan. Walaupun mendokumentasikan sangat penting, ia juga mengharapkan pengembang yang mengonsumsi proaktif atau defensif dan untuk benar-benar membaca dokumentasi secara terperinci. Saya akan memilih kesalahan ramah / deskriptif atas dokumentasi yang baik karena pengguna akhir memiliki kesempatan untuk melihat salah satu dari mereka dan bukan yang lain; ada kesempatan yang lebih baik untuk memiliki pengguna akhir mengkomunikasikan kesalahan deskriptif kepada programmer miskin daripada programmer miskin membaca dokumen lengkap
STW
4
Catatan, jika Anda menangkap ArgumentException, itu juga akan menangkap ArgumentOutOfRange.
Steven Evers
Bagaimana dengan FormatException: Pengecualian yang dilemparkan ketika format argumen tidak valid, atau ketika string format komposit tidak terbentuk dengan baik.
Anthony
44

Saya memilih jawaban Josh , tetapi ingin menambahkan satu lagi ke daftar:

System.InvalidOperationException harus dilemparkan jika argumen tersebut valid, tetapi objek berada dalam keadaan di mana argumen tidak boleh digunakan.

Pembaruan Diambil dari MSDN:

InvalidOperationException digunakan dalam kasus ketika kegagalan untuk memanggil metode disebabkan oleh alasan selain argumen yang tidak valid.

Katakanlah objek Anda memiliki metode PerformAction (enmSomeAction action), enmSomeActions yang valid adalah Buka dan Tutup. Jika Anda memanggil PerformAction (enmSomeAction.Open) dua kali berturut-turut maka panggilan kedua harus melempar InvalidOperationException (karena arugment valid, tetapi tidak untuk keadaan kontrol saat ini)

Karena Anda sudah melakukan hal yang benar dengan pemrograman defensif saya punya satu pengecualian lain untuk menyebutkan adalah ObjectDisposedException. Jika objek Anda mengimplementasikan IDisposable maka Anda harus selalu memiliki variabel kelas yang melacak keadaan dibuang; jika objek Anda telah dibuang dan sebuah metode dipanggil, Anda harus meningkatkan ObjectDisposedException:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Pembaruan: Untuk menjawab tindak lanjut Anda: Ini adalah situasi yang agak ambigu, dan dibuat sedikit lebih rumit oleh tipe data generik (bukan dalam pengertian .NET Generics) yang digunakan untuk mewakili sekumpulan data tertentu; enum atau objek yang sangat diketik lainnya akan lebih cocok - tetapi kita tidak selalu memiliki kontrol itu.

Saya pribadi akan condong ke ArgumentOutOfRangeException dan memberikan pesan yang menunjukkan nilai yang valid adalah 1-12. Alasan saya adalah bahwa ketika Anda berbicara tentang bulan, dengan asumsi semua representasi bilangan bulat bulan valid, maka Anda mengharapkan nilai dalam kisaran 1-12. Jika hanya bulan-bulan tertentu (seperti bulan yang memiliki 31 hari) yang valid maka Anda tidak akan berurusan dengan Range per-se dan saya akan membuang ArgumentException umum yang menunjukkan nilai-nilai yang valid, dan saya juga akan mendokumentasikannya dalam komentar metode.

STW
sumber
1
Poin yang bagus. Ini mungkin menjelaskan perbedaan antara input yang tidak valid dan tidak diekspektasi. +1
Daniel Brückner
Psst, saya setuju dengan Anda hanya tidak akan mencuri guntur Anda. Tetapi karena Anda menunjukkannya, saya memperbarui jawaban saya
JoshBerke
38

Bergantung pada nilai aktual dan pengecualian apa yang paling cocok:

Jika ini tidak cukup tepat, turunkan saja kelas pengecualian Anda sendiri ArgumentException.

Jawaban Yoooder mencerahkan saya. Input tidak valid jika tidak valid kapan saja, sedangkan input tidak terduga jika tidak valid untuk kondisi sistem saat ini. Jadi dalam kasus selanjutnya, an InvalidOperationExceptionadalah pilihan yang masuk akal.

Daniel Brückner
sumber
6
Diambil dari halaman MSDN di InvalidOperationException: "InvalidOperationException digunakan dalam kasus-kasus ketika kegagalan untuk memanggil metode disebabkan oleh alasan selain dari argumen yang tidak valid."
STW
4

pengecualian argumen.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException
Syed Tayyab Ali
sumber
3

ArgumentException :

ArgumentException dilemparkan ketika suatu metode dipanggil dan setidaknya satu dari argumen yang dilewati tidak memenuhi spesifikasi parameter dari metode yang dipanggil. Semua instance ArgumentException harus membawa pesan kesalahan yang bermakna yang menguraikan argumen yang tidak valid, serta kisaran nilai yang diharapkan untuk argumen.

Beberapa subclass juga ada untuk jenis ketidakabsahan tertentu. Tautan ini memiliki ringkasan subtipe dan kapan harus berlaku.

Ben S
sumber
1

Jawaban singkat:
Baik

Jawaban yang lebih panjang:
menggunakan Argumen * Pengecualian (kecuali di perpustakaan yang merupakan produk di atasnya, seperti perpustakaan komponen) adalah bau. Pengecualian adalah untuk menangani situasi luar biasa, bukan bug, dan bukan kekurangan pengguna (yaitu konsumen API).

Jawaban terlama:
Melontarkan pengecualian untuk argumen yang tidak valid adalah kasar, kecuali Anda menulis perpustakaan.
Saya lebih suka menggunakan pernyataan, karena dua (atau lebih) alasan:

  • Pernyataan tidak perlu diuji, sementara pernyataan melemparkan, dan pengujian terhadap ArgumentNullException terlihat konyol (coba saja).
  • Pernyataan lebih baik mengkomunikasikan tujuan penggunaan unit, dan lebih dekat menjadi dokumentasi yang dapat dieksekusi daripada spesifikasi perilaku kelas.
  • Anda bisa mengubah perilaku pelanggaran asersi. Misalnya dalam kompilasi debug, kotak pesan baik-baik saja, sehingga QA Anda akan langsung menerimanya dengan Anda (Anda juga mendapatkan IDE Anda melanggar pada garis di mana itu terjadi), sedangkan dalam unit test Anda dapat menunjukkan kegagalan pernyataan sebagai kegagalan pengujian .

Berikut ini adalah bagaimana penanganan pengecualian null (menjadi sarkastik, jelas):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

Pengecualian harus digunakan ketika situasi diharapkan tetapi luar biasa (terjadi hal-hal yang di luar kendali konsumen, seperti kegagalan IO). Argumen * Pengecualian adalah indikasi bug dan akan (pendapat saya) ditangani dengan tes dan dibantu dengan Debug.

BTW: Dalam kasus khusus ini, Anda bisa menggunakan tipe Bulan, bukan int. C # gagal ketika harus mengetikkan safety (Aspect # rulez!) Tetapi terkadang Anda dapat mencegah (atau menangkap pada waktu kompilasi) semua bug tersebut secara bersamaan.

Dan ya, MicroSoft salah tentang itu.

THX-1138
sumber
6
IMHO, pengecualian juga harus dilemparkan ketika metode yang dipanggil tidak dapat melanjutkan secara wajar. Itu termasuk kasus ketika penelepon telah mengeluarkan argumen palsu. Apa yang akan kamu lakukan? Kembali -1?
John Saunders
1
Jika argumen tidak valid akan menyebabkan fungsi dalam gagal, apa pro dan kontra dari pengujian argumen untuk validitas, dibandingkan dengan menangkap InvalidArgumentException dari fungsi dalam dan membungkusnya dengan yang lebih informatif? Pendekatan yang terakhir tampaknya akan meningkatkan kinerja dalam kasus umum, tetapi saya belum melihatnya melakukan banyak hal.
supercat
Pencarian google cepat di sekitar pertanyaan ini menunjukkan bahwa melemparkan Pengecualian umum adalah praktik terburuk dari semua. Sehubungan dengan pernyataan argumen, saya melihat manfaat dalam hal ini pada proyek-proyek pribadi kecil, tetapi tidak pada aplikasi perusahaan di mana argumen yang tidak valid kemungkinan besar disebabkan oleh konfigurasi yang buruk atau pemahaman aplikasi yang buruk.
Maks
0

Ada ArgumentException standar yang bisa Anda gunakan, atau Anda bisa membuat subkelas dan membuatnya sendiri. Ada beberapa kelas ArgumentException tertentu:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Mana yang paling berhasil.

Scott M.
sumber
1
Saya tidak setuju untuk hampir semua kasus; argumen. * Perkecualian .NET yang disediakan sangat umum digunakan dan memberi Anda kemampuan untuk memberikan informasi spesifik yang cukup untuk memberi tahu konsumen tentang masalah ini.
STW
Untuk mengklarifikasi - Saya tidak setuju untuk hampir semua kasus yang berasal dari kelas Argument * Exception. Menggunakan salah satu Pengecualian Argumen .NET plus pesan yang deskriptif dan jelas memberikan detail yang cukup untuk lebih atau kurang setiap situasi di mana argumen tidak valid.
STW
setuju, tetapi saya hanya menjelaskan opsi yang tersedia. Saya jelas harus lebih jelas tentang metode "disukai".
Scott M.