Pertanyaan ini dimaksudkan untuk diterapkan pada bahasa pemrograman OO yang mendukung penanganan pengecualian; Saya menggunakan C # untuk tujuan ilustrasi saja.
Pengecualian biasanya dimaksudkan untuk dimunculkan ketika masalah muncul bahwa kode tidak dapat segera ditangani, dan kemudian ditangkap dalam catch
klausa di lokasi yang berbeda (biasanya bingkai tumpukan luar).
T: Apakah ada situasi yang sah di mana pengecualian tidak dilemparkan dan ditangkap, tetapi hanya dikembalikan dari metode dan kemudian diedarkan sebagai objek kesalahan?
Pertanyaan ini muncul untuk saya karena System.IObserver<T>.OnError
metode .NET 4 menyarankan hanya itu: pengecualian dilewatkan sebagai objek kesalahan.
Mari kita lihat skenario lain, validasi. Katakanlah saya mengikuti kebijakan konvensional, dan karena itu saya membedakan antara tipe objek kesalahan IValidationError
dan tipe pengecualian terpisah ValidationException
yang digunakan untuk melaporkan kesalahan tak terduga:
partial interface IValidationError { }
abstract partial class ValidationException : System.Exception
{
public abstract IValidationError[] ValidationErrors { get; }
}
( System.Component.DataAnnotations
Namespace melakukan sesuatu yang sangat mirip.)
Jenis-jenis ini dapat digunakan sebagai berikut:
partial interface IFoo { } // an immutable type
partial interface IFooBuilder // mutable counterpart to prepare instances of above type
{
bool IsValid(out IValidationError[] validationErrors); // true if no validation error occurs
IFoo Build(); // throws ValidationException if !IsValid(…)
}
Sekarang saya bertanya-tanya, dapatkah saya menyederhanakan hal di atas menjadi ini:
partial class ValidationError : System.Exception { } // = IValidationError + ValidationException
partial interface IFoo { } // (unchanged)
partial interface IFooBuilder
{
bool IsValid(out ValidationError[] validationErrors);
IFoo Build(); // may throw ValidationError or sth. like AggregateException<ValidationError>
}
T: Apa kelebihan dan kekurangan dari dua pendekatan yang berbeda ini?
AggregateException
? Poin bagus.Jawaban:
Jika tidak pernah dibuang maka itu bukan pengecualian. Ini adalah objek
derived
dari kelas Exception, meskipun tidak mengikuti perilaku. Menyebutnya Pengecualian adalah semantik murni pada titik ini, tetapi saya tidak melihat ada yang salah dengan tidak melemparkannya. Dari sudut pandang saya, tidak melempar pengecualian adalah hal yang persis sama dengan fungsi dalam menangkapnya dan mencegahnya menyebar.Apakah valid untuk fungsi untuk mengembalikan objek pengecualian?
Benar. Berikut adalah daftar contoh singkat di mana ini mungkin sesuai:
Bukankah membuangnya buruk?
Melempar pengecualian adalah seperti: "Pergi ke penjara. Jangan lulus Pergi!" di papan permainan Monopoli. Ini memberitahu kompiler untuk melompati semua kode sumber hingga tangkapan tanpa mengeksekusi salah satu kode sumber itu. Itu tidak ada hubungannya dengan kesalahan, pelaporan atau menghentikan bug. Saya suka berpikir untuk melempar pengecualian sebagai pernyataan "super return" untuk suatu fungsi. Ini mengembalikan eksekusi kode ke suatu tempat yang jauh lebih tinggi dari pemanggil asli.
Yang penting di sini adalah untuk memahami bahwa nilai sebenarnya dari pengecualian ada dalam
try/catch
pola, dan bukan instantiasi objek pengecualian. Objek pengecualian hanyalah pesan status.Dalam pertanyaan Anda, Anda tampaknya membingungkan penggunaan dua hal ini: melompat ke pengendali pengecualian, dan status kesalahan yang diwakili oleh pengecualian. Hanya karena Anda mengambil status kesalahan dan membungkusnya dalam pengecualian tidak berarti Anda mengikuti
try/catch
pola atau manfaatnya.sumber
Exception
kelas tetapi tidak mengikuti perilaku." - Dan itulah masalahnya: Apakah sah untuk menggunakanException
objek yang diarsipkan dalam situasi di mana ia tidak akan terlempar sama sekali, baik oleh produsen objek, maupun dengan metode panggilan apa pun; di mana itu hanya berfungsi sebagai "pesan negara" yang diedarkan, tanpa aliran kontrol "super return"? Atau apakah saya melanggar kontraktry
/ tidak terucapkancatch(Exception)
begitu buruk sehingga saya tidak boleh melakukan ini, dan bukannya menggunakan, non-Exception
tipe yang terpisah ?No
Anda tidak melanggar kontrak apa pun. Thepopular opinion
akan bahwa Anda tidak harus melakukan itu. Pemrograman penuh dengan contoh-contoh di mana kode sumber Anda tidak melakukan kesalahan tetapi semua orang tidak menyukainya. Kode sumber harus selalu ditulis agar jelas maksudnya. Apakah Anda benar-benar membantu programmer lain dengan menggunakan pengecualian seperti itu? Jika ya, maka lakukanlah. Kalau tidak, jangan terkejut jika itu tidak disukai.Mengembalikan pengecualian alih-alih melemparkannya dapat masuk akal secara semantik ketika Anda memiliki metode penolong untuk menganalisis situasi dan mengembalikan pengecualian yang sesuai yang kemudian dilontarkan oleh penelepon (Anda dapat menyebutnya sebagai "pabrik pengecualian"). Melempar pengecualian dalam fungsi analisa kesalahan ini akan berarti bahwa ada kesalahan selama analisis itu sendiri, sementara mengembalikan pengecualian berarti jenis kesalahan dianalisis dengan sukses.
Salah satu use case yang mungkin adalah fungsi yang mengubah kode respons HTTP menjadi pengecualian:
Perhatikan bahwa melempar pengecualian berarti bahwa metode yang digunakan salah atau memiliki kesalahan internal, sementara mengembalikan pengecualian berarti bahwa kode kesalahan diidentifikasi berhasil.
sumber
return
pernyataan berlebihan untuk membuat kompiler berhenti mengeluh.System.Exit()
. Kode setelah panggilan fungsi tidak dieksekusi. Itu tidak terdengar seperti pola desain yang bagus untuk suatu fungsi.Secara konseptual, jika objek pengecualian adalah hasil yang diharapkan dari operasi, maka ya. Kasus-kasus yang dapat saya pikirkan, bagaimanapun, selalu melibatkan lemparan-tangkapan di beberapa titik:
Variasi pada pola "Coba" (di mana metode merangkum metode melempar pengecualian kedua, tetapi menangkap pengecualian dan sebaliknya mengembalikan boolean yang menunjukkan keberhasilan). Anda bisa, alih-alih mengembalikan Boolean, mengembalikan Pengecualian apa pun yang dilemparkan (nol jika berhasil), memungkinkan pengguna akhir lebih banyak informasi sambil mempertahankan indikasi keberhasilan atau kegagalan hasil tangkapan.
Pemrosesan kesalahan alur kerja. Mirip dalam praktiknya dengan enkapsulasi metode "coba pola", Anda mungkin memiliki langkah alur kerja yang diabstraksi dalam pola rantai-perintah. Jika tautan dalam rantai melempar pengecualian, sering kali lebih bersih untuk menangkap pengecualian itu dalam objek abstrak, lalu kembalikan sebagai hasil dari operasi tersebut sehingga mesin alur kerja dan kode aktual dijalankan sebagai langkah alur kerja tidak perlu dicoba secara ekstensif Logika -catch mereka sendiri.
sumber
OperationResult
kelas abstrak , denganbool
properti "Sukses",GetExceptionIfAny
metode, danAssertSuccessful
metode (yang akan membuang pengecualian dariGetExceptionIfAny
jika tidak nol). MemilikiGetExceptionIfAny
metode daripada properti akan memungkinkan penggunaan objek kesalahan statis yang tidak dapat diubah untuk kasus-kasus di mana tidak banyak yang berguna untuk dikatakan.Katakanlah Anda memerlukan beberapa tugas untuk dieksekusi di beberapa kumpulan utas. Jika tugas ini melempar pengecualian, ini utas berbeda sehingga Anda tidak menangkapnya. Thread di mana itu dieksekusi mati.
Sekarang pertimbangkan bahwa sesuatu (baik kode dalam tugas itu, atau implementasi thread pool) menangkap pengecualian yang menyimpannya bersama dengan tugas dan mempertimbangkan tugas selesai (tidak berhasil). Sekarang Anda dapat bertanya apakah tugas sudah selesai (dan apakah bertanya apakah itu melempar, atau dapat dilemparkan lagi di utas Anda (atau lebih baik, pengecualian baru dengan sumber asli sebagai penyebab)).
Jika Anda melakukannya secara manual, Anda dapat melihat bahwa Anda sedang membuat pengecualian baru, melempar dan menangkapnya, lalu menyimpannya, di berbagai utas yang mengambil, melempar, menangkap dan bereaksi padanya. Jadi masuk akal untuk melewatkan lempar dan tangkap dan simpan saja dan selesaikan tugas lalu tanyakan apakah ada pengecualian dan bereaksi padanya. Tapi ini bisa mengarah pada kode yang lebih rumit jika di tempat yang sama bisa ada pengecualian.
PS: ini ditulis dengan pengalaman dengan Java, di mana informasi jejak stack dibuat ketika pengecualian dibuat, (tidak seperti C # di mana ia dibuat saat melempar). Jadi pendekatan pengecualian tidak dilempar di Jawa akan lebih lambat dari pada di C # (kecuali yang telah diolah dan digunakan kembali), tetapi akan memiliki info jejak stack yang tersedia.
Secara umum, saya akan tinggal jauh dari membuat pengecualian dan tidak pernah membuangnya (kecuali jika optimalisasi kinerja setelah profiling menunjukkannya sebagai bottleneck). Paling tidak di java, di mana pembuatan pengecualian mahal (tumpukan jejak). Dalam C # itu adalah kemungkinan, tetapi IMO itu mengejutkan dan karenanya harus dihindari.
sumber
Tergantung pada desain Anda, biasanya saya tidak akan mengembalikan pengecualian ke penelepon, melainkan saya akan melempar dan menangkapnya dan membiarkannya begitu saja. Biasanya kode ditulis untuk gagal lebih awal dan cepat. Sebagai contoh, pertimbangkan kasus membuka file dan memprosesnya (Ini adalah C # PsuedoCode):
Dalam hal ini, kami akan melakukan kesalahan dan berhenti memproses file segera setelah menemukan catatan buruk.
Tetapi katakan persyaratannya adalah kami ingin terus memproses file bahkan jika satu atau lebih baris memiliki kesalahan. Kode mungkin mulai terlihat seperti ini:
Jadi dalam hal ini kita mengembalikan pengecualian kembali ke penelepon, meskipun saya mungkin tidak akan mengembalikan pengecualian tetapi sebaliknya semacam objek kesalahan karena pengecualian telah ditangkap dan sudah ditangani. Ini tidak ada salahnya mengembalikan kembali pengecualian, tetapi penelepon lain dapat melemparkan kembali pengecualian yang mungkin tidak diinginkan karena objek pengecualian memiliki perilaku itu. Jika Anda ingin penelepon memiliki kemampuan untuk melemparkan kembali maka kembalikan pengecualian, jika tidak, keluarkan informasi dari objek pengecualian dan buat objek yang lebih ringan dan kembalikan. Gagal cepat biasanya kode bersih.
Sebagai contoh validasi Anda, saya mungkin tidak akan mewarisi dari kelas pengecualian atau melempar pengecualian karena kesalahan validasi bisa sangat umum. Akan lebih murah untuk mengembalikan objek daripada melemparkan pengecualian jika 50% pengguna saya gagal mengisi formulir dengan benar pada upaya pertama.
sumber
Iya. Sebagai contoh, saya baru-baru ini menghadapi situasi situasi di mana saya menemukan bahwa melemparkan Pengecualian dalam ASMX Web Service tidak menyertakan elemen dalam pesan SOAP yang dihasilkan, jadi saya harus membuatnya.
Kode ilustrasi:
sumber
Dalam kebanyakan bahasa (afaik), pengecualian datang dengan beberapa barang tambahan. Sebagian besar, snapshot dari jejak stack saat ini disimpan di objek. Ini bagus untuk debugging, tetapi juga dapat memiliki implikasi memori.
Seperti yang sudah dikatakan @ThinkingMedia, Anda benar-benar menggunakan pengecualian sebagai objek kesalahan.
Dalam contoh kode Anda, sepertinya Anda melakukan ini terutama untuk penggunaan kembali kode dan untuk menghindari komposisi. Saya pribadi tidak berpikir ini adalah alasan yang bagus untuk melakukan ini.
Alasan lain yang mungkin adalah untuk menipu bahasa agar memberi Anda objek kesalahan dengan jejak tumpukan. Ini memberikan informasi kontekstual tambahan untuk kode penanganan kesalahan Anda.
Di sisi lain, kita dapat mengasumsikan bahwa menjaga jejak stack di sekitar memiliki biaya memori. Misalnya jika Anda mulai mengumpulkan objek-objek ini di suatu tempat, Anda mungkin melihat dampak memori yang tidak menyenangkan. Tentu saja ini sangat tergantung pada bagaimana jejak stack pengecualian diimplementasikan dalam bahasa / mesin masing-masing.
Jadi, apakah itu ide yang bagus?
Sejauh ini saya belum melihatnya digunakan atau direkomendasikan. Tetapi ini tidak berarti banyak.
Pertanyaannya adalah: Apakah jejak tumpukan benar-benar berguna untuk penanganan kesalahan Anda? Atau lebih umum, informasi apa yang ingin Anda berikan kepada pengembang atau administrator?
Pola lemparan / coba / tangkap yang khas membuatnya perlu untuk memiliki jejak tumpukan, karena jika tidak Anda tidak tahu dari mana asalnya. Untuk objek yang dikembalikan, selalu berasal dari fungsi yang disebut. Jejak tumpukan mungkin berisi semua informasi yang dibutuhkan pengembang, tetapi mungkin sesuatu yang kurang berat dan lebih spesifik sudah cukup.
sumber
Jawabannya iya. Lihat http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions dan buka panah untuk panduan gaya C ++ Google tentang pengecualian. Anda dapat melihat di sana argumen yang mereka gunakan untuk memutuskan, tetapi katakan bahwa jika mereka harus melakukannya lagi, mereka kemungkinan akan memutuskan untuk melakukannya.
Namun perlu dicatat bahwa bahasa Go juga tidak secara idiomatis menggunakan pengecualian, untuk alasan yang sama.
sumber