Seperti yang ditunjukkan oleh contoh Anda, setiap kasing harus dievaluasi secara terpisah dan terdapat spektrum abu-abu yang cukup besar antara "keadaan luar biasa" dan "kontrol aliran", terutama jika metode Anda dimaksudkan untuk dapat digunakan kembali, dan dapat digunakan dalam pola yang sangat berbeda. daripada yang awalnya dirancang untuk. Jangan berharap kita semua di sini setuju tentang arti "tidak luar biasa", terutama jika Anda segera mendiskusikan kemungkinan menggunakan "pengecualian" untuk mengimplementasikannya.
Kami mungkin juga tidak menyetujui desain apa yang membuat kode paling mudah dibaca dan dipelihara, tetapi saya akan berasumsi bahwa perancang perpustakaan memiliki visi pribadi yang jelas tentang itu dan hanya perlu menyeimbangkannya dengan pertimbangan lain yang terlibat.
Jawaban singkat
Ikuti perasaan Anda kecuali ketika Anda merancang metode yang sangat cepat dan mengharapkan kemungkinan penggunaan kembali yang tidak terduga.
Jawaban panjang
Setiap penelepon masa depan dapat dengan bebas menerjemahkan antara kode kesalahan dan pengecualian sesuai keinginan di kedua arah; ini membuat dua pendekatan desain hampir setara kecuali untuk kinerja, keramahan debugger dan beberapa konteks interoperabilitas terbatas. Ini biasanya bermuara pada kinerja, jadi mari kita fokus pada hal itu.
Sebagai aturan praktis, berharap bahwa melempar pengecualian adalah 200x lebih lambat dari pengembalian reguler (pada kenyataannya, ada perbedaan yang signifikan dalam hal itu).
Sebagai aturan praktis lainnya, melempar pengecualian mungkin sering memungkinkan kode yang lebih bersih dibandingkan dengan nilai sihir yang paling kasar, karena Anda tidak bergantung pada pemrogram yang menerjemahkan kode kesalahan ke dalam kode kesalahan lain, karena ia berjalan melalui beberapa lapis kode klien menuju suatu titik di mana ada konteks yang cukup untuk menanganinya dengan cara yang konsisten dan memadai. (Kasus khusus: null
cenderung lebih baik di sini daripada nilai sihir lainnya karena kecenderungannya untuk secara otomatis menerjemahkan dirinya ke NullReferenceException
dalam kasus beberapa, tetapi tidak semua jenis cacat; biasanya, tetapi tidak selalu, cukup dekat dengan sumber cacat. )
Jadi apa pelajarannya?
Untuk fungsi yang dipanggil hanya beberapa kali selama masa aplikasi (seperti inisialisasi aplikasi), gunakan apa pun yang memberi Anda kode yang lebih bersih dan mudah dipahami. Kinerja tidak bisa menjadi perhatian.
Untuk fungsi membuang, gunakan apa pun yang memberi Anda kode bersih. Kemudian lakukan beberapa profil (jika diperlukan sama sekali) dan ubah pengecualian untuk mengembalikan kode jika mereka termasuk di antara kemacetan teratas yang dicurigai berdasarkan pada pengukuran atau keseluruhan struktur program.
Untuk fungsi yang dapat digunakan kembali yang mahal, gunakan apa pun yang memberi Anda kode bersih. Jika pada dasarnya Anda selalu harus menjalani pulang-pergi jaringan atau mem-parsing file XML pada disk, biaya overhead untuk melempar pengecualian mungkin dapat diabaikan. Lebih penting untuk tidak kehilangan detail kegagalan apa pun, bahkan tidak sengaja, daripada kembali dari "kegagalan luar biasa" ekstra cepat.
Fungsi lean yang dapat digunakan kembali membutuhkan lebih banyak pemikiran. Dengan menggunakan pengecualian, Anda memaksa sesuatu seperti 100 kali melambat pada penelepon yang akan melihat pengecualian pada setengah dari (banyak) panggilan mereka, jika tubuh fungsi dijalankan dengan sangat cepat. Pengecualian masih merupakan opsi desain, tetapi Anda harus memberikan alternatif overhead rendah untuk penelepon yang tidak mampu membayar ini. Mari kita lihat sebuah contoh.
Anda mencantumkan contoh yang bagus Dictionary<,>.Item
, yang, secara longgar, berubah dari mengembalikan null
nilai menjadi melempar KeyNotFoundException
antara. NET 1.1 dan .NET 2.0 (hanya jika Anda mau dianggap Hashtable.Item
sebagai pelopor non-generik yang praktis). Alasan "perubahan" ini bukan tanpa minat di sini. Optimalisasi kinerja tipe nilai (tidak ada lagi tinju) menjadikan nilai sulap asli ( null
) bukan pilihan; out
parameter hanya akan membawa sebagian kecil dari biaya kinerja kembali. Pertimbangan kinerja yang terakhir ini benar-benar dapat diabaikan dibandingkan dengan overhead dari melempar a KeyNotFoundException
, tetapi desain pengecualian masih lebih unggul di sini. Mengapa?
- parameter ref / out dikenakan biaya setiap kali, tidak hanya dalam kasus "kegagalan"
- Siapa pun yang peduli dapat menelepon
Contains
sebelum panggilan apa pun ke pengindeks, dan pola ini dibaca secara alami. Jika pengembang ingin tetapi lupa menelepon Contains
, tidak ada masalah kinerja yang dapat merambah; KeyNotFoundException
cukup keras untuk diperhatikan dan diperbaiki.