Penggunaan blok coba / tangkap yang efisien?

21

Haruskah blok tangkap digunakan untuk menulis logika yaitu menangani kontrol aliran dll? Atau hanya untuk melempar pengecualian? Apakah ini memengaruhi efisiensi atau pemeliharaan kode?

Apa efek samping (jika ada) dari logika penulisan di catch block?

EDIT:

Saya telah melihat kelas Java SDK di mana mereka telah menulis logika di dalam blok tangkap. Misalnya (cuplikan diambil dari java.lang.Integerkelas):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Saya telah melalui tutorial di mana mereka menghitungnya sebagai keuntungan dari penulisan logika kasus luar biasa di dalam pengecualian:

Pengecualian memungkinkan Anda untuk menulis alur utama kode Anda dan untuk menangani kasus luar biasa di tempat lain.

Adakah pedoman khusus kapan menulis logika di catch block dan kapan tidak?

HashimR
sumber
12
@Coder Saya pikir Anda terlalu generalisasi sedikit. Terutama karena dengan banyak API yang ada, Anda tidak dapat menghindarinya.
CodesInChaos
8
@ Kode: Di Jawa pengecualian sering digunakan sebagai mekanisme untuk memberi isyarat masalah yang merupakan bagian dari aliran kode yang sah. Misalnya, jika Anda mencoba untuk membuka file dan gagal, perpustakaan Java memberitahu Anda bahwa dengan melemparkan pengecualian, karena API yang mengarah ke membuka file tidak memiliki mekanisme lain untuk mengembalikan kesalahan.
JeremyP
4
@ Kode Tergantung. Saya pikir ketika melakukan IO, atau ketika parsing data yang kompleks, yang hampir selalu diformat dengan benar, melemparkan pengecualian untuk menandakan kesalahan membuat kode jauh lebih bersih.
CodesInChaos
1
@Code Ya, eksekusi ada untuk metode untuk memberi tahu Anda itu tidak dapat melakukan apa yang diminta. Tetapi pengecualian tidak boleh digunakan untuk kontrol aliran, itulah sebabnya C # juga memiliki metode int.TryParse yang tidak melempar.
Andy
1
@ Kode: Itu benar-benar sampah. Hal-hal yang luar biasa pada satu tingkat kode mungkin tidak luar biasa pada yang lain- atau Anda mungkin ingin, misalnya, menyajikan atau menambahkan kesalahan atau informasi debug sebelum melanjutkan.
DeadMG

Jawaban:

46

Contoh yang Anda kutip disebabkan oleh desain API yang buruk (tidak ada cara bersih untuk memeriksa apakah sebuah String adalah integer yang valid kecuali mencoba menguraikannya dan menangkap pengecualian).

Di tingkat teknis, lempar dan coba / tangkap adalah konstruksi aliran kontrol yang memungkinkan Anda untuk melompat ke tumpukan panggilan, tidak lebih dan tidak kurang. Melompati tumpukan panggilan secara implisit menghubungkan kode yang tidak berdekatan dalam sumber, yang buruk untuk pemeliharaan . Jadi itu hanya boleh digunakan ketika Anda perlu melakukan itu dan alternatifnya bahkan lebih buruk. The kasus yang diterima secara luas di mana alternatif yang lebih buruk adalah penanganan kesalahan (kembali kode khusus yang perlu diperiksa dan melewatkan setiap tingkat tumpukan panggilan secara manual).

Jika Anda memiliki kasus di mana alternatif lebih buruk (dan Anda benar-benar telah mempertimbangkan semuanya dengan hati-hati), maka saya akan mengatakan menggunakan lemparan dan coba / tangkap untuk aliran kontrol baik-baik saja.Dogma bukan pengganti yang baik untuk penilaian.

Michael Borgwardt
sumber
1
+1, poin bagus dibuat. Saya pikir beberapa pemrosesan mungkin valid dalam tangkapan seperti persiapan pesan kesalahan dan kesalahan logging. Membebaskan sumber daya yang mahal seperti koneksi db dan dalam (.NET) referensi COM dapat ditambahkan ke blok Akhirnya.
NoChance
@EmmadKareem Saya berpikir tentang membebaskan sumber daya, tetapi biasanya Anda harus membebaskannya di beberapa titik bahkan tanpa situasi kesalahan. Dengan demikian Anda bisa mengulangi sendiri. Mempersiapkan pesan log tentu saja dapat diterima.
scarfridge
@MichaelBorgwardt Saya pikir desain API tidak begitu buruk (meskipun bisa lebih baik). Mencoba mengurai string yang tidak valid jelas merupakan kondisi kesalahan dan dengan demikian persis "kasus yang diterima secara luas" yang Anda sebutkan. Setuju, sebuah metode untuk menentukan apakah suatu string secara sintaksis angka yang tepat berguna (ini adalah di mana itu bisa lebih baik). Namun, memaksa semua orang untuk memanggil metode ini sebelum parsing yang sebenarnya tidak mungkin dan kami perlu menangani kesalahan. Mencampur hasil aktual dan kode kesalahan tidak nyaman dan Anda masih akan membuang pengecualian. Tapi mungkin pengecualian runtime.
scarfridge
3
+1 untuk kalimat terakhir itu.
FrustratedWithFormsDesigner
2
@ scarfridge: Saya setuju sepenuhnya dengan Anda :) Tidak adanya metode isInteger (String) yang mengembalikan boolean adalah yang saya maksud dengan "desain API yang buruk"
Michael Borgwardt
9

Ini adalah sesuatu yang cenderung bergantung pada bahasa dan paradigma.

Sebagian besar pekerjaan saya dilakukan di Jawa (dan terkadang C ++). Kecenderungannya adalah menggunakan pengecualian untuk kondisi luar biasa saja. Ada beberapa pertanyaan tentang Stack Overflow tentang overhead kinerja pengecualian di Jawa , dan seperti yang Anda lihat, itu tidak bisa diabaikan. Ada juga kekhawatiran lain, seperti keterbacaan dan pemeliharaan kode. Jika digunakan dengan benar, pengecualian dapat mendelegasikan penanganan kesalahan ke komponen yang sesuai.

Namun, dalam Python, gagasan bahwa lebih mudah untuk meminta maaf daripada memerintah. Di komunitas Python, ini dikenal sebagai EAFP , yang kontras dengan pendekatan "look before you leap" ( LBYL ) dalam bahasa gaya-C.

Thomas Owens
sumber
2
+1 untuk menyebutkan overhead dengan pengecualian. Saya lakukan. NET dan (setidaknya pada mesin saya) saya bahkan bisa merasakan ketika pengecualian akan terjadi pada durasi yang dibutuhkan dalam beberapa kasus dibandingkan dengan operasi normal lainnya.
NoChance
2
@EmmadKareem Hanya jika Anda memiliki debugger terpasang. Anda dapat membuang beberapa ribu dari mereka per detik tanpa debugger.
CodesInChaos
@ CodeInChaos, Anda benar. Bagaimana saya tahu, saya menulis kode bersih yang tidak pernah didapat pada saat dijalankan :)
NoChance
@EmmadKareem Tergantung pada bagaimana seseorang menulis aplikasi jaringan, kegagalan koneksi atau protokol ditangani dengan pengecualian. Pengecualian harus cukup cepat dalam aplikasi semacam itu untuk menghindari serangan DoS yang sepele.
CodesInChaos
@ CodeInChaos, ini bagus untuk diketahui. Saya bercanda dalam komentar terakhir saya.
NoChance
9

Itu bukan cara berpikir terbaik tentang coba / tangkap balok. Cara untuk memikirkan blok coba / tangkap adalah ini: kegagalan telah terjadi, di mana tempat terbaik untuk menangani kegagalan spesifik ini. Itu mungkin baris kode berikutnya, mungkin dua puluh tingkat di atas rantai panggilan. Dimanapun itu, di situlah seharusnya.

Apa yang dilakukan oleh catch block tergantung pada kesalahan dan apa yang dapat Anda lakukan. Kadang-kadang hanya dapat diabaikan (kegagalan untuk menghapus file awal tanpa data penting di dalamnya), kadang-kadang akan digunakan untuk mengatur nilai kembali fungsi ke true atau false (metode int parse java), kadang-kadang Anda akan keluar dari program . Semua itu tergantung kesalahan.

Bagian penting untuk dipahami adalah: catch block == Saya tahu cara menghadapinya.

jmoreno
sumber
6

Blok penangkap seharusnya hanya digunakan untuk menangani pengecualian, tidak ada yang lain dan tidak pernah untuk aliran kontrol. Dalam situasi apa pun di mana Anda ingin menggunakan pengecualian untuk mengelola aliran Anda akan lebih baik memeriksa pra-kondisi.

Menangani aliran dengan pengecualian lambat dan secara semantik salah.

Tom Squires
sumber
4
Saya tahu apa yang Anda katakan, tetapi perlu dicatat bahwa semua yang dikecualikan adalah menangani aliran program - hanya saja itu seharusnya hanya digunakan untuk mengontrol aliran kesalahan yang luar biasa ...
Max
Apakah tidak ada pedoman khusus kapan harus menulis logika di catch block dan kapan tidak?
HashimR
4
Pengurai Angka Java dan pemformat Teks adalah contoh terkenal dari penggunaan pengecualian bermasalah: satu-satunya cara yang masuk akal untuk memeriksa prasyarat (bahwa string mewakili angka yang dapat diuraikan) adalah dengan mencoba menguraikannya, dan jika gagal dengan pengecualian, apa pilihan Anda dalam praktek? Anda harus menangkap pengecualian dan mengelola aliran dengan itu, tidak peduli itu dianggap semantik salah.
Joonas Pulakka
@JoonasPulakka Saya pikir komentar Anda wrt Number parser dan Text formatters memenuhi syarat sebagai jawaban ukuran penuh untuk pertanyaan ini
Agak
5

Haruskah blok tangkap digunakan untuk menulis logika yaitu menangani kontrol aliran dll? Atau hanya untuk melempar pengecualian? Apakah ini memengaruhi efisiensi atau pemeliharaan kode?

Pertama, lupakan gagasan bahwa "pengecualian harus digunakan untuk kondisi luar biasa". Juga berhenti khawatir tentang efisiensi, sampai Anda memiliki kode yang berkinerja tidak dapat diterima dan Anda telah diukur untuk mengetahui di mana masalahnya.

Kode paling mudah dipahami ketika tindakan mengikuti dalam urutan sederhana, tanpa persyaratan. Pengecualian meningkatkan pemeliharaan dengan menghapus pengecekan kesalahan dari aliran normal. Tidak masalah jika eksekusi mengikuti aliran normal 99,9% dari waktu atau 50% dari waktu atau 20% dari waktu.

Melempar pengecualian ketika suatu fungsi tidak dapat mengembalikan nilai, ketika prosedur tidak dapat menyelesaikan tindakan yang diharapkan, atau ketika konstruktor tidak dapat menghasilkan objek yang dapat digunakan. Hal ini memungkinkan pemrogram untuk berasumsi bahwa suatu fungsi selalu mengembalikan hasil yang dapat digunakan, prosedur selalu menyelesaikan tindakan yang diminta, objek yang dibangun selalu dalam keadaan dapat digunakan.

Masalah terbesar yang saya lihat dengan pengecualian kode penanganan adalah programmer menulis try / catch blocks padahal seharusnya tidak. Misalnya, di sebagian besar aplikasi web, jika permintaan basis data melempar pengecualian, tidak ada yang bisa dilakukan di controller. Satu klausa tangkapan generik pada level tertinggi sudah cukup. Kemudian kode pengontrol dapat mengabaikan kemungkinan bahwa disk mengalami crash atau database sedang offline atau apa pun.

kevin cline
sumber
1

Tangkapan blok tidak boleh digunakan untuk menulis logika kode. Mereka harus digunakan hanya untuk menangani kesalahan. Contohnya adalah (1) membersihkan semua sumber daya yang dialokasikan, (2) mencetak pesan yang bermanfaat, dan (3) keluar dengan anggun.

Memang benar bahwa pengecualian dapat disalahgunakan dan menyebabkan gotoefek yang tidak diinginkan . Tapi itu tidak membuat mereka tidak berguna. Ketika digunakan dengan benar , mereka dapat meningkatkan banyak aspek kode Anda.

sakisk
sumber