Mengapa tidak menggunakan pengecualian sebagai aliran kontrol biasa?

192

Untuk menghindari semua jawaban standar yang saya bisa gunakan di Google, saya akan memberikan contoh yang Anda semua bisa serang sesuka hati.

C # dan Java (dan terlalu banyak yang lain) miliki dengan banyak tipe beberapa perilaku 'overflow' yang saya tidak suka sama sekali (misalnya misalnya type.MaxValue + type.SmallestValue == type.MinValue:) int.MaxValue + 1 == int.MinValue.

Tapi, melihat sifat jahat saya, saya akan menambahkan beberapa penghinaan untuk cedera ini dengan memperluas perilaku ini, katakanlah DateTimetipe Overridden . (Saya tahu DateTimedisegel dalam. NET, tetapi demi contoh ini, saya menggunakan bahasa semu yang persis seperti C #, kecuali untuk fakta bahwa DateTime tidak disegel).

AddMetode yang diganti :

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

Tentu saja jika bisa menyelesaikan ini dengan mudah, tetapi kenyataannya tetap saja saya gagal melihat mengapa Anda tidak dapat menggunakan pengecualian (secara logis artinya, saya dapat melihat bahwa ketika kinerja merupakan masalah yang dalam beberapa kasus pengecualian harus dihindari. ).

Saya pikir dalam banyak kasus mereka lebih jelas daripada jika-struktur dan tidak melanggar kontrak apa pun yang dibuat metode ini.

IMHO reaksi "Jangan pernah menggunakannya untuk aliran program reguler" yang tampaknya dimiliki semua orang tidak terlalu buruk karena kekuatan reaksi itu dapat dibenarkan.

Atau saya salah?

Saya sudah membaca posting lain, berurusan dengan semua jenis kasus khusus, tetapi poin saya adalah tidak ada yang salah dengan itu jika Anda berdua:

  1. Bersih
  2. Hormati kontrak metode Anda

Tembak saya.

Peter
sumber
2
+1 Saya merasakan hal yang sama. Selain kinerja, satu-satunya alasan bagus untuk menghindari pengecualian-untuk-kontrol-aliran adalah ketika kode pemanggil akan jauh lebih mudah dibaca dengan nilai pengembalian.
4
apakah: return -1 jika sesuatu terjadi, return -2 jika sesuatu yang lain, dll ... benar-benar lebih mudah dibaca daripada pengecualian?
kender
1
Sangat menyedihkan bahwa seseorang mendapat reputasi negatif karena mengatakan yang sebenarnya: Bahwa teladan Anda tidak mungkin ditulis dengan pernyataan if. (Ini bukan untuk mengatakan itu benar / lengkap.)
Ingo
8
Saya berpendapat, bahwa melemparkan pengecualian kadang-kadang menjadi satu-satunya pilihan Anda. Saya sudah misalnya komponen bisnis yang menginisialisasi keadaan internal dalam konstruktornya dengan menanyakan database. Ada kalanya, ketika tidak ada data yang sesuai dalam database tersedia. Melempar pengecualian dalam konstruktor adalah satu-satunya cara untuk secara efektif membatalkan konstruksi objek. Ini jelas dinyatakan dalam kontrak (Javadoc dalam kasus saya) kelas, jadi saya tidak punya masalah bahwa kode klien dapat (dan harus) menangkap pengecualian itu ketika membuat komponen dan melanjutkan dari sana.
Stefan Haberl
1
Karena Anda merumuskan hipotesis, tanggung jawab ada pada Anda untuk juga mengutip bukti / alasan yang menguatkan. Sebagai permulaan, sebutkan satu alasan mengapa kode Anda lebih unggul daripada ifpernyataan yang mendokumentasikan diri sendiri jauh lebih pendek . Anda akan menemukan ini sangat sulit. Dengan kata lain: premis Anda sendiri cacat, dan kesimpulan yang Anda ambil darinya salah.
Konrad Rudolph

Jawaban:

169

Pernahkah Anda mencoba men-debug program yang menaikkan lima pengecualian per detik dalam operasi normal?

Saya sudah.

Program ini cukup kompleks (itu adalah server perhitungan terdistribusi), dan sedikit modifikasi di satu sisi program dapat dengan mudah memecahkan sesuatu di tempat yang sama sekali berbeda.

Saya berharap saya bisa saja meluncurkan program dan menunggu pengecualian terjadi, tetapi ada sekitar 200 pengecualian selama permulaan dalam operasi normal

Maksud saya: jika Anda menggunakan pengecualian untuk situasi normal, bagaimana Anda menemukan situasi yang tidak biasa (yaitu pengecualian al)?

Tentu saja, ada alasan kuat lain untuk tidak menggunakan terlalu banyak pengecualian, terutama dari segi kinerja

Brann
sumber
13
Contoh: ketika saya men-debug program .net, saya meluncurkannya dari visual studio dan saya meminta VS untuk menghentikan semua pengecualian. Jika Anda mengandalkan pengecualian sebagai perilaku yang diharapkan, saya tidak bisa melakukannya lagi (karena itu akan merusak 5 kali / detik), dan jauh lebih rumit untuk menemukan bagian kode yang bermasalah.
Brann
15
+1 untuk menunjukkan Anda tidak ingin membuat tumpukan jerami pengecualian untuk menemukan jarum luar biasa yang sebenarnya.
Grant Wagner
16
tidak mendapatkan jawaban ini sama sekali, saya pikir orang salah paham di sini Ini tidak ada hubungannya dengan debugging sama sekali, tetapi dengan desain. Ini adalah alasan melingkar dalam bentuk pures itu aku takut. Dan poin Anda benar-benar selain pertanyaan seperti yang dinyatakan sebelumnya
Peter
11
@ Peter: Melakukan debugging tanpa merusak pengecualian itu sulit, dan menangkap semua pengecualian itu menyakitkan jika ada banyak dari mereka dengan desain. Saya pikir desain yang membuat debugging sulit hampir rusak sebagian (dengan kata lain, desain HAS ada hubungannya dengan debugging, IMO)
Brann
7
Bahkan mengabaikan fakta bahwa sebagian besar situasi yang ingin saya debug tidak sesuai dengan pengecualian yang dilemparkan, jawaban untuk pertanyaan Anda adalah: "berdasarkan jenis", misalnya, saya akan memberi tahu debugger saya untuk menangkap hanya AssertionError atau StandardError atau sesuatu yang tidak sesuai dengan hal-hal buruk yang terjadi. Jika Anda memiliki masalah dengan itu, lalu bagaimana cara Anda melakukan logging - tidakkah Anda login berdasarkan level dan kelas, sehingga Anda bisa memfilternya? Apakah Anda pikir itu ide yang buruk juga?
Ken,
157

Pengecualian pada dasarnya adalah gotopernyataan non-lokal dengan semua konsekuensi dari yang terakhir. Menggunakan pengecualian untuk kontrol aliran melanggar prinsip yang paling mengejutkan , membuat program sulit dibaca (ingat bahwa program ditulis untuk programmer terlebih dahulu).

Selain itu, ini bukan yang diharapkan oleh vendor kompiler. Mereka mengharapkan pengecualian untuk jarang dilempar, dan mereka biasanya membiarkan throwkode menjadi sangat tidak efisien. Melontarkan pengecualian adalah salah satu operasi paling mahal di .NET.

Namun, beberapa bahasa (terutama Python) menggunakan pengecualian sebagai konstruk kontrol aliran. Misalnya, iterator memunculkan StopIterationpengecualian jika tidak ada item lebih lanjut. Bahkan konstruksi bahasa standar (seperti for) mengandalkan ini.

Anton Gogolev
sumber
11
hei, pengecualian tidak mengherankan! Dan Anda agak bertentangan dengan diri Anda ketika Anda mengatakan "itu ide yang buruk" dan kemudian berkata "tapi itu ide yang bagus dengan python".
Hasen
6
Saya masih tidak yakin sama sekali: 1) Efisiensi ada di samping pertanyaan, banyak program non-bacht tidak peduli (misalnya antarmuka pengguna) 2) Mengagumkan: seperti saya katakan itu hanya mengherankan karena tidak digunakan, tetapi pertanyaannya tetap: mengapa tidak menggunakan id di tempat pertama? Tapi, karena ini adalah jawabannya
Peter
4
+1 Sebenarnya saya senang Anda menunjukkan perbedaan antara Python dan C #. Saya tidak berpikir itu kontradiksi. Python jauh lebih dinamis dan harapan untuk menggunakan pengecualian dengan cara ini dimasukkan ke dalam bahasa. Itu juga bagian dari budaya EAFP Python. Saya tidak tahu pendekatan mana yang lebih murni secara konseptual atau lebih konsisten, tetapi saya menyukai gagasan menulis kode yang melakukan apa yang orang lain harapkan untuk dilakukan, yang berarti gaya berbeda dalam bahasa yang berbeda.
Mark E. Haase
13
Berbeda dengan goto, tentu saja, perkecualian berinteraksi dengan benar dengan tumpukan panggilan Anda dan dengan pelingkupan leksikal, dan jangan meninggalkan tumpukan atau cakupan dalam kekacauan.
Lukas Eder
4
Sebenarnya, sebagian besar vendor VM mengharapkan pengecualian, dan menanganinya secara efisien. Sebagai catatan @LukasEder pengecualian sama sekali tidak seperti goto dalam bahwa mereka terstruktur.
Marcin
29

Aturan praktis saya adalah:

  • Jika Anda dapat melakukan apa saja untuk memulihkan dari kesalahan, tangkap pengecualian
  • Jika kesalahannya sangat umum (mis. Pengguna mencoba masuk dengan kata sandi yang salah), gunakan nilai balik
  • Jika Anda tidak dapat melakukan apa pun untuk memulihkan dari kesalahan, biarkan itu tidak tertangkap (Atau tangkap di penangkap utama Anda untuk melakukan penutupan aplikasi secara semi-anggun)

Masalah yang saya lihat dengan pengecualian adalah dari sudut pandang sintaksis murni (saya cukup yakin kinerja overhead minimal). Saya tidak suka blok percobaan di semua tempat.

Ambil contoh ini:

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

.. Contoh lain bisa terjadi ketika Anda perlu menetapkan sesuatu ke pegangan menggunakan pabrik, dan pabrik itu bisa mengeluarkan pengecualian:

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

Jadi, secara pribadi, saya pikir Anda harus menyimpan pengecualian untuk kondisi kesalahan yang jarang terjadi (kehabisan memori, dll.) Dan menggunakan nilai balik (kacamata nilai, struct, atau enum) untuk melakukan pengecekan kesalahan.

Semoga saya mengerti pertanyaan Anda dengan benar :)

cwap
sumber
4
re: Contoh kedua Anda - mengapa tidak langsung saja menelepon ke BestMethodEver di dalam blok try, setelah Build? Jika Build () melempar pengecualian, itu tidak akan dieksekusi, dan kompilernya senang.
Blorgbeard keluar dari
2
Yup, itu mungkin yang akan Anda dapatkan, tetapi pertimbangkan contoh yang lebih kompleks di mana tipe myInstance itu sendiri dapat melempar pengecualian .. Dan isntance lain dalam cakupan metode juga bisa. Anda akan berakhir dengan banyak blok mencoba / menangkap bersarang :(
cwap
Anda harus melakukan terjemahan Exception (ke tipe Exception yang sesuai dengan tingkat abstraksi) di catch catch Anda. FYI: "Multi-menangkap" seharusnya pergi ke Jawa 7.
jasonnerothin
FYI: Di C ++, Anda dapat menempatkan beberapa tangkapan setelah mencoba menangkap beberapa pengecualian.
RobH
2
Untuk perangkat lunak shrinkwrap, Anda perlu menangkap semua pengecualian. Setidaknya pasang kotak dialog yang menjelaskan bahwa program perlu dimatikan, dan ini adalah sesuatu yang tidak dapat dipahami yang dapat Anda kirim dalam laporan bug.
David Thornley
25

Reaksi pertama terhadap banyak jawaban:

Anda menulis untuk para programmer dan prinsip paling tidak heran

Tentu saja! Tetapi jika tidak selalu lebih jelas setiap saat.

Seharusnya tidak mengherankan misalnya: membagi (1 / x) menangkap (pembagianByZero) lebih jelas daripada jika saya (di Conrad dan lain-lain). Fakta pemrograman semacam ini tidak diharapkan murni konvensional, dan memang, masih relevan. Mungkin dalam contoh saya jika akan lebih jelas.

Tapi DivisionByZero dan FileNotFound dalam hal ini lebih jelas daripada seandainya.

Tentu saja jika itu kurang berkinerja dan membutuhkan miliaran waktu per detik, Anda tentu saja harus menghindarinya, tetapi saya masih belum membaca alasan bagus untuk menghindari desain keseluruhan.

Sejauh prinsip yang paling mengejutkan: ada bahaya penalaran melingkar di sini: seandainya seluruh komunitas menggunakan desain yang buruk, desain ini akan menjadi yang diharapkan! Karena itu prinsipnya tidak bisa menjadi cawan dan harus dipertimbangkan dengan hati-hati.

pengecualian untuk situasi normal, bagaimana Anda menemukan situasi yang tidak biasa (yaitu luar biasa)?

Dalam banyak reaksi, sth. seperti ini bersinar palung. Tangkap saja, bukan? Metode Anda harus jelas, didokumentasikan dengan baik, dan menunggu kontraknya. Saya tidak mendapatkan pertanyaan yang harus saya akui.

Debugging pada semua pengecualian: sama, itu hanya dilakukan kadang-kadang karena desain untuk tidak menggunakan pengecualian adalah umum. Pertanyaan saya adalah: mengapa itu biasa terjadi?

Peter
sumber
1
1) Apakah Anda selalu memeriksa xsebelum menelepon 1/x? 2) Apakah Anda membungkus setiap operasi divisi menjadi blok try-catch untuk menangkap DivideByZeroException? 3) Apa logika yang Anda masukkan ke dalam blok penangkap untuk pulih DivideByZeroException?
Lightman
1
Kecuali DivisionByZero dan FileNotFound adalah contoh buruk karena mereka adalah kasus luar biasa yang harus diperlakukan sebagai pengecualian.
0x6C38
Tidak ada "terlalu luar biasa" tentang file-tidak-ditemukan dengan cara selain "anti-pengecualian" orang di sini menggembar-gemborkan. openConfigFile();bisa diikuti oleh FileNotFound yang tertangkap dengan { createDefaultConfigFile(); setFirstAppRun(); }pengecualian FileNotFound yang ditangani dengan anggun; tidak crash, mari kita buat pengalaman pengguna akhir lebih baik, tidak lebih buruk. Anda mungkin berkata, "Tetapi bagaimana jika ini bukan benar-benar menjalankan pertama dan mereka mendapatkan itu setiap waktu?" Setidaknya aplikasi berjalan setiap waktu dan tidak menabrak setiap startup! Pada 1-ke-10 "ini mengerikan": "jalankan pertama" setiap startup = 3 atau 4, crash setiap startup = 10.
Loduwijk
Contoh Anda adalah pengecualian. Tidak, Anda tidak selalu memeriksa xsebelum menelepon 1/x, karena biasanya baik-baik saja. Kasus luar biasa adalah kasus di mana itu tidak baik. Kita tidak berbicara tentang penghancuran bumi di sini, tetapi misalnya untuk bilangan bulat dasar yang diberikan secara acak x, hanya 1 dari 4294967296 yang gagal melakukan pembagian. Itu luar biasa dan pengecualian adalah cara yang baik untuk menghadapinya. Namun, Anda bisa menggunakan pengecualian untuk mengimplementasikan pernyataan yang setara switch, tetapi itu akan sangat konyol.
Thor84no
16

Sebelum pengecualian, di C, ada setjmpdan longjmpyang bisa digunakan untuk menyelesaikan pembukaan gulungan frame stack yang serupa.

Kemudian konstruksi yang sama diberi nama: "Pengecualian". Dan sebagian besar jawaban bergantung pada makna nama ini untuk berdebat tentang penggunaannya, mengklaim bahwa pengecualian dimaksudkan untuk digunakan dalam kondisi luar biasa. Itu tidak pernah menjadi maksud dalam aslinya longjmp. Hanya ada situasi di mana Anda perlu memutus aliran kontrol di banyak bingkai tumpukan.

Pengecualian sedikit lebih umum karena Anda dapat menggunakannya dalam bingkai tumpukan yang sama juga. Ini menimbulkan analogi dengan gotoyang saya percaya salah. Foto adalah pasangan yang sangat erat (dan begitu juga setjmpdan longjmp). Pengecualian mengikuti publikasi / berlangganan yang digabungkan secara longgar yang jauh lebih bersih! Oleh karena itu menggunakannya dalam bingkai tumpukan yang sama hampir tidak sama dengan menggunakan gotos.

Sumber kebingungan ketiga berhubungan dengan apakah mereka diperiksa atau tidak dicentang pengecualian. Tentu saja, pengecualian yang tidak diperiksa tampaknya sangat mengerikan untuk digunakan untuk aliran kontrol dan mungkin banyak hal lainnya.

Namun, pengecualian yang dicek sangat cocok untuk aliran kontrol, setelah Anda mengatasi semua masalah Victoria dan hidup sedikit.

Penggunaan favorit saya adalah urutan throw new Success()dalam fragmen panjang kode yang mencoba satu demi satu sampai menemukan apa yang dicari. Setiap hal - masing-masing bagian dari logika - mungkin bersarang secara acak sehingga breakkeluar juga segala jenis tes kondisi. The if-elsepola rapuh. Jika saya mengedit elseatau mengacaukan sintaks dengan cara lain, maka ada bug berbulu.

Menggunakan throw new Success() linierisasi aliran kode. Saya menggunakan Successkelas yang didefinisikan secara lokal - diperiksa tentu saja - sehingga jika saya lupa menangkapnya, kode tidak akan dikompilasi. Dan saya tidak menangkap Successes metode lain .

Terkadang kode saya memeriksa satu dan lain hal dan hanya berhasil jika semuanya baik-baik saja. Dalam hal ini saya menggunakan linearisasi serupa throw new Failure().

Menggunakan fungsi terpisah mengacaukan tingkat alami kompartementalisasi. Jadi returnsolusinya tidak optimal. Saya lebih suka memiliki satu atau dua halaman kode di satu tempat karena alasan kognitif. Saya tidak percaya pada kode yang dibagi sangat halus.

Apa yang dilakukan JVM atau kompiler kurang relevan bagi saya kecuali ada hotspot. Saya tidak percaya ada alasan mendasar bagi penyusun untuk tidak mendeteksi Pengecualian yang dilemparkan dan ditangkap secara lokal dan memperlakukannya sebagai sangat efisien gotopada tingkat kode mesin.

Sejauh menggunakannya di seluruh fungsi untuk aliran kontrol - yaitu untuk kasus umum dan bukan yang luar biasa - Saya tidak bisa melihat bagaimana mereka akan kurang efisien daripada beberapa istirahat, tes kondisi, kembali ke mengarungi tiga frame stack sebagai lawan hanya mengembalikan penunjuk tumpukan.

Saya pribadi tidak menggunakan pola di seluruh bingkai tumpukan dan saya bisa melihat bagaimana itu membutuhkan kecanggihan desain untuk melakukannya dengan elegan. Tetapi jika digunakan dengan hemat, itu tidak masalah.

Terakhir, mengenai programmer perawan yang mengejutkan, itu bukan alasan yang meyakinkan. Jika Anda dengan lembut mengenalkan mereka pada latihan, mereka akan belajar untuk menyukainya. Saya ingat C ++ digunakan untuk mengejutkan dan menakut-nakuti programmer C.

ahli nujum
sumber
3
Dengan menggunakan pola ini, sebagian besar fungsi kasar saya memiliki dua tangkapan kecil di akhir - satu untuk Sukses dan satu untuk Kegagalan dan di situlah fungsi tersebut merangkum hal-hal seperti menyiapkan respons servlet yang benar atau menyiapkan nilai pengembalian. Memiliki satu tempat untuk menyelesaikannya bagus. The returnalternatif -pattern akan membutuhkan dua fungsi untuk setiap fungsi tersebut. Yang luar untuk mempersiapkan respon servlet atau tindakan lain seperti itu, dan yang dalam untuk melakukan perhitungan. PS: Seorang profesor bahasa Inggris mungkin akan menyarankan saya menggunakan "mencengangkan" daripada "mengejutkan" dalam paragraf terakhir :-)
necromancer
11

Server standar adalah pengecualian tidak teratur dan harus digunakan dalam kasus luar biasa.

Salah satu alasan, yang penting bagi saya, adalah bahwa ketika saya membaca try-catchstruktur kontrol dalam perangkat lunak yang saya kelola atau debug, saya mencoba mencari tahu mengapa pembuat kode asli menggunakan penanganan pengecualian alih-alih if-elsestruktur. Dan saya berharap menemukan jawaban yang bagus.

Ingatlah bahwa Anda menulis kode tidak hanya untuk komputer tetapi juga untuk pembuat kode lain. Ada semantik yang terkait dengan penangan pengecualian yang tidak dapat Anda buang hanya karena mesin tidak keberatan.

mouviciel
sumber
Jawaban yang kurang dihargai saya pikir. Komputer mungkin tidak melambat banyak ketika menemukan pengecualian sedang ditelan tetapi ketika saya sedang mengerjakan kode orang lain dan saya menemukan itu menghentikan saya mati di trek saya sementara saya bekerja jika saya telah melewatkan sesuatu yang penting yang saya tidak lakukan tidak tahu, atau jika sebenarnya tidak ada pembenaran untuk penggunaan anti-pola ini.
Tim Abell
9

Bagaimana dengan kinerja? Saat memuat pengujian aplikasi web .NET, kami berhasil mencapai 100 pengguna simulasi per server web hingga kami memperbaiki pengecualian yang biasa terjadi dan jumlahnya meningkat menjadi 500 pengguna.

James Koch
sumber
8

Josh Bloch membahas topik ini secara luas di Java Efektif. Sarannya mencerahkan dan harus berlaku untuk Net juga (kecuali untuk rinciannya).

Khususnya, pengecualian harus digunakan untuk keadaan luar biasa. Alasan untuk ini terkait dengan kegunaan, terutama. Agar metode yang diberikan dapat digunakan secara maksimal, kondisi input dan outputnya harus dibatasi secara maksimal.

Misalnya, metode kedua lebih mudah digunakan daripada yang pertama:

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 * @throws AdditionException if addend1 or addend2 is less than or equal to zero
 */
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
  if( addend1 <= 0 ){
     throw new AdditionException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new AdditionException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 */
public int addPositiveNumbers(int addend1, int addend2) {
  if( addend1 <= 0 ){
     throw new IllegalArgumentException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new IllegalArgumentException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

Dalam kedua kasus tersebut, Anda perlu memeriksa untuk memastikan bahwa pemanggil menggunakan API Anda dengan tepat. Tetapi dalam kasus kedua, Anda memerlukannya (secara implisit). Pengecualian lunak akan tetap dilemparkan jika pengguna tidak membaca javadoc, tetapi:

  1. Anda tidak perlu mendokumentasikannya.
  2. Anda tidak perlu mengujinya (tergantung seberapa agresif strategi pengujian unit Anda).
  3. Anda tidak mengharuskan penelepon untuk menangani tiga kasus penggunaan.

Titik permukaan tanah adalah bahwa Pengecualian tidak boleh digunakan sebagai kode pengembalian, sebagian besar karena Anda tidak hanya rumit API ANDA, tetapi API penelepon juga.

Tentu saja melakukan hal yang benar harus dibayar. Biayanya adalah semua orang perlu memahami bahwa mereka perlu membaca dan mengikuti dokumentasi. Semoga itulah masalahnya.

jasonnerothin
sumber
7

Saya pikir Anda bisa menggunakan Pengecualian untuk kontrol aliran. Namun, ada sisi negatif dari teknik ini. Membuat Pengecualian adalah hal yang mahal, karena mereka harus membuat jejak tumpukan. Jadi, jika Anda ingin menggunakan Pengecualian lebih sering daripada hanya menandakan situasi yang luar biasa, Anda harus memastikan bahwa membangun jejak tumpukan tidak memengaruhi kinerja Anda secara negatif.

Cara terbaik untuk mengurangi biaya membuat pengecualian adalah dengan mengganti metode fillInStackTrace () seperti ini:

public Throwable fillInStackTrace() { return this; }

Pengecualian seperti itu tidak akan memiliki stacktraces diisi.

paweloque
sumber
Stacktrace juga mengharuskan penelepon untuk "tahu tentang" (yaitu memiliki ketergantungan pada) semua Throwables dalam stack. Ini adalah hal yang buruk. Lempar pengecualian yang sesuai dengan tingkat abstraksi (ServiceExceptions in Services, DaoExceptions from Dao methods, dll). Cukup terjemahkan jika perlu.
jasonnerothin
5

Saya tidak benar-benar melihat bagaimana Anda mengendalikan aliran program dalam kode yang Anda kutip. Anda tidak akan pernah melihat pengecualian lain selain pengecualian ArgumentOutOfRange. (Jadi klausa tangkapan kedua Anda tidak akan pernah mengenai). Yang Anda lakukan hanyalah menggunakan lemparan yang sangat mahal untuk meniru pernyataan if.

Anda juga tidak melakukan operasi yang lebih menyeramkan di mana Anda hanya melemparkan pengecualian semata-mata untuk ditangkap di tempat lain untuk melakukan kontrol aliran. Anda sebenarnya menangani kasus luar biasa.

Jason Punyon
sumber
5

Berikut adalah praktik terbaik yang saya jelaskan di posting blog saya :

  • Lempar pengecualian untuk menyatakan situasi yang tidak terduga dalam perangkat lunak Anda.
  • Gunakan nilai balik untuk validasi input .
  • Jika Anda tahu cara menangani pengecualian yang dilemparkan perpustakaan, tangkap mereka pada tingkat serendah mungkin .
  • Jika Anda memiliki pengecualian yang tidak terduga, buang operasi saat ini sepenuhnya. Jangan pura-pura tahu cara menghadapinya .
Vladimir
sumber
4

Karena kode ini sulit dibaca, Anda mungkin mengalami masalah saat debugging, Anda akan memperkenalkan bug baru ketika memperbaiki bug setelah waktu yang lama, itu lebih mahal dalam hal sumber daya dan waktu, dan itu mengganggu Anda jika Anda men-debug kode Anda dan debugger menghentikan kemunculan setiap pengecualian;)

Gambrinus
sumber
4

Terlepas dari alasan yang disebutkan, satu alasan untuk tidak menggunakan pengecualian untuk kontrol aliran adalah bahwa hal itu dapat sangat mempersulit proses debugging.

Misalnya, ketika saya mencoba melacak bug di VS saya biasanya akan mengaktifkan "break on all exception". Jika Anda menggunakan pengecualian untuk kontrol aliran maka saya akan melanggar debugger secara teratur dan harus terus mengabaikan pengecualian tidak luar biasa ini sampai saya sampai pada masalah sebenarnya. Ini mungkin membuat seseorang marah !!

Sean
sumber
1
Saya sudah menangani yang lebih tinggi: Debugging pada semua pengecualian: sama, itu hanya dilakukan karena desain untuk tidak menggunakan pengecualian adalah umum. Pertanyaan saya adalah: mengapa itu biasa terjadi?
Peter
Jadi, apakah jawaban Anda pada dasarnya "Ini buruk karena Visual Studio memiliki fitur yang satu ini ..."? Saya telah pemrograman selama sekitar 20 tahun sekarang, dan saya bahkan tidak menyadari bahwa ada opsi "break on all exception". Tetap saja, "karena fitur yang satu ini!" Kedengarannya seperti alasan yang lemah. Lacak saja pengecualian ke sumbernya; semoga Anda menggunakan bahasa yang memudahkan ini - jika tidak, masalah Anda adalah pada fitur bahasa, bukan dengan penggunaan umum pengecualian itu sendiri.
Loduwijk
3

Mari kita asumsikan Anda memiliki metode yang melakukan beberapa perhitungan. Ada banyak parameter input yang harus divalidasi, lalu untuk mengembalikan angka lebih besar dari 0.

Menggunakan nilai kembali ke kesalahan validasi sinyal, itu sederhana: jika metode mengembalikan angka lebih kecil dari 0, kesalahan terjadi. Bagaimana cara mengetahui parameter mana yang tidak divalidasi?

Saya ingat dari hari C saya banyak fungsi mengembalikan kode kesalahan seperti ini:

-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY

dll.

Apakah ini benar-benar kurang mudah dibaca daripada melempar dan menangkap pengecualian?

kender
sumber
itu sebabnya mereka menemukan enum :) Tapi angka ajaib adalah topik yang sama sekali berbeda .. en.wikipedia.org/wiki/…
Isak Savo
Contoh yang bagus Saya akan menulis hal yang sama. @IsakSavo: Enums tidak membantu dalam situasi ini jika metode ini diharapkan untuk mengembalikan beberapa nilai makna atau objek. Misalnya getAccountBalance () harus mengembalikan objek Uang, bukan objek AccountBalanceResultEnum. Banyak program C memiliki pola yang sama di mana satu nilai sentinel (0 atau nol) mewakili kesalahan, dan kemudian Anda harus memanggil fungsi lain untuk mendapatkan kode kesalahan yang terpisah untuk menentukan mengapa kesalahan terjadi. (MySQL C API seperti ini.)
Mark E. Haase
3

Anda dapat menggunakan cakar palu untuk memutar sekrup, sama seperti Anda dapat menggunakan pengecualian untuk aliran kontrol. Itu tidak berarti itu adalah penggunaan fitur yang dimaksudkan. The ifmengekspresikan pernyataan kondisi, yang dimaksudkan penggunaan yang mengendalikan aliran.

Jika Anda menggunakan fitur dengan cara yang tidak disengaja saat memilih untuk tidak menggunakan fitur yang dirancang untuk tujuan itu, akan ada biaya yang terkait. Dalam hal ini, kejelasan dan kinerja menderita tanpa nilai tambah nyata. Apa yang menggunakan pengecualian membeli Anda dari ifpernyataan yang diterima secara luas ?

Mengatakan dengan cara lain: hanya karena Anda tidak dapat berarti Anda harus melakukannya .

Bryan Watts
sumber
1
Apakah Anda mengatakan bahwa tidak perlu pengecualian, setelah kami mendapatkan ifuntuk penggunaan normal atau penggunaan eksekusi tidak dimaksudkan karena tidak dimaksudkan (argumen melingkar)?
Val
1
@Val: Pengecualian untuk situasi luar biasa - jika kami mendeteksi cukup untuk melempar pengecualian dan menanganinya, kami memiliki informasi yang cukup untuk tidak membuangnya dan masih menanganinya. Kita bisa langsung menuju ke logika penanganan dan melewatkan percobaan / tangkapan yang mahal dan berlebihan.
Bryan Watts
Dengan logika itu, Anda mungkin juga tidak memiliki pengecualian dan selalu melakukan sistem keluar daripada melemparkan pengecualian. Jika Anda ingin melakukan sesuatu sebelum keluar, maka buat bungkusnya dan panggil itu. Contoh Java: public class ExitHelper{ public static void cleanExit() { cleanup(); System.exit(1); } }Lalu panggil saja itu alih-alih melempar: ExitHelper.cleanExit();Jika argumen Anda masuk akal, maka ini akan menjadi pendekatan yang disukai dan tidak akan ada pengecualian. Anda pada dasarnya mengatakan, "Satu-satunya alasan pengecualian adalah jatuh dengan cara yang berbeda."
Loduwijk
@ Harun: Jika saya melempar dan menangkap pengecualian, saya punya cukup informasi untuk menghindari hal itu. Itu tidak berarti bahwa semua pengecualian tiba-tiba berakibat fatal. Kode lain yang tidak saya kontrol mungkin menangkapnya, dan itu bagus. Argumen saya, yang tetap sehat, adalah bahwa melempar dan menangkap pengecualian dalam konteks yang sama berlebihan. Saya tidak, juga tidak, menyatakan bahwa semua pengecualian harus keluar dari proses.
Bryan Watts
@BryanWatts Diakui. Banyak orang lain telah mengatakan bahwa Anda hanya harus menggunakan pengecualian untuk sesuatu yang tidak dapat pulih dari, dan dengan ekstensi harus selalu menerjang di pengecualian. Inilah sebabnya mengapa sulit untuk membahas hal-hal ini; tidak hanya ada 2 pendapat, tetapi banyak. Saya masih tidak setuju dengan Anda, tetapi tidak kuat. Ada saat-saat ketika melempar / menangkap bersama adalah kode yang paling mudah dibaca, dipelihara, dan tampak terbaik; biasanya ini terjadi jika Anda sudah menangkap pengecualian lain sehingga Anda sudah mencoba / menangkap dan menambahkan 1 atau 2 tangkapan lebih bersih daripada memisahkan pemeriksaan kesalahan oleh if.
Loduwijk
3

Seperti yang disebutkan orang lain secara berulang-ulang, prinsip ketakjuban akan melarang Anda menggunakan pengecualian secara berlebihan untuk tujuan kontrol aliran saja. Di sisi lain, tidak ada aturan yang 100% benar, dan selalu ada kasus-kasus di mana pengecualian adalah "alat yang tepat" - sama seperti gotoitu sendiri, omong-omong, yang dikirimkan dalam bentuk breakdan continuedalam bahasa seperti Jawa, yang sering kali merupakan cara sempurna untuk melompat keluar dari lilitan bersarang, yang tidak selalu dapat dihindari.

Posting blog berikut menjelaskan kasus penggunaan yang agak rumit tetapi juga menarik untuk non-lokal ControlFlowException :

Ini menjelaskan bagaimana di dalam jOOQ (pustaka abstraksi SQL untuk Java) , pengecualian seperti itu kadang-kadang digunakan untuk membatalkan proses rendering SQL lebih awal ketika beberapa kondisi "langka" terpenuhi.

Contoh dari kondisi tersebut adalah:

  • Nilai ikatan terlalu banyak ditemui. Beberapa basis data tidak mendukung angka bind nilai sembarang dalam pernyataan SQL mereka (SQLite: 999, Ingres 10.1.0: 1024, Sybase ASE 15.5: 2000, SQL Server 2008: 2100). Dalam kasus-kasus tersebut, JOOQ membatalkan fase rendering SQL dan merender kembali pernyataan SQL dengan nilai-nilai binding yang digarisbawahi. Contoh:

    // Pseudo-code attaching a "handler" that will
    // abort query rendering once the maximum number
    // of bind values was exceeded:
    context.attachBindValueCounter();
    String sql;
    try {
    
      // In most cases, this will succeed:
      sql = query.render();
    }
    catch (ReRenderWithInlinedVariables e) {
      sql = query.renderWithInlinedBindValues();
    }

    Jika kami secara eksplisit mengekstraksi nilai ikat dari kueri AST untuk menghitungnya setiap waktu, kami akan membuang siklus CPU yang berharga untuk 99,9% kueri yang tidak mengalami masalah ini.

  • Beberapa logika hanya tersedia secara tidak langsung melalui API yang kami ingin jalankan hanya "sebagian". The UpdatableRecord.store()metode menghasilkan INSERTatau UPDATEpernyataan, tergantung pada Recordbendera internal. Dari "luar", kita tidak tahu jenis logika apa yang terkandung store()(misalnya penguncian optimistis, penanganan pendengar acara, dll.) Sehingga kami tidak ingin mengulangi logika itu ketika kami menyimpan beberapa catatan dalam pernyataan kumpulan, di mana kami ingin store()hanya menghasilkan pernyataan SQL, tidak benar-benar menjalankannya. Contoh:

    // Pseudo-code attaching a "handler" that will
    // prevent query execution and throw exceptions
    // instead:
    context.attachQueryCollector();
    
    // Collect the SQL for every store operation
    for (int i = 0; i < records.length; i++) {
      try {
        records[i].store();
      }
    
      // The attached handler will result in this
      // exception being thrown rather than actually
      // storing records to the database
      catch (QueryCollectorException e) {
    
        // The exception is thrown after the rendered
        // SQL statement is available
        queries.add(e.query());                
      }
    }

    Jika kami telah meng-eksternal-kan store()logikanya ke dalam API "yang dapat digunakan kembali" yang dapat dikustomisasi untuk tidak mengeksekusi SQL, kami akan mencari cara membuat API yang agak sulit dipertahankan dan sulit digunakan kembali.

Kesimpulan

Intinya, penggunaan non-lokal gotoini sesuai dengan apa yang dikatakan [Mason Wheeler] [5] dalam jawabannya:

"Saya baru saja menghadapi situasi yang tidak dapat saya tangani dengan benar pada saat ini, karena saya tidak memiliki cukup konteks untuk menanganinya, tetapi rutinitas yang memanggil saya (atau sesuatu yang lebih jauh dari tumpukan panggilan) harus tahu bagaimana menanganinya . "

Kedua penggunaan ControlFlowExceptionsagak mudah diimplementasikan dibandingkan dengan alternatif mereka, memungkinkan kami untuk menggunakan kembali berbagai logika tanpa refactoring keluar dari internal yang relevan.

Tapi perasaan ini menjadi sedikit kejutan bagi pemelihara masa depan tetap ada. Kode terasa agak halus dan meskipun itu adalah pilihan yang tepat dalam kasus ini, kami selalu memilih untuk tidak menggunakan pengecualian untuk aliran kontrol lokal , di mana mudah untuk menghindari menggunakan percabangan biasa if - else.

Lukas Eder
sumber
2

Biasanya tidak ada yang salah, per se, dengan menangani pengecualian pada level rendah. Pengecualian ADALAH pesan yang valid yang memberikan banyak detail mengapa operasi tidak dapat dilakukan. Dan jika Anda bisa mengatasinya, Anda harus melakukannya.

Secara umum jika Anda tahu ada kemungkinan kegagalan yang tinggi yang dapat Anda periksa ... Anda harus melakukan pemeriksaan ... yaitu if (obj! = Null) obj.method ()

Dalam kasus Anda, saya tidak cukup terbiasa dengan pustaka C # untuk mengetahui apakah waktu tanggal memiliki cara mudah untuk memeriksa apakah cap waktu berada di luar batas. Jika ya, panggil saja if (.isvalid (ts)) jika tidak, kode Anda pada dasarnya baik-baik saja.

Jadi, pada dasarnya turun ke cara mana saja menciptakan kode bersih ... jika operasi untuk menjaga terhadap pengecualian yang diharapkan lebih kompleks daripada hanya menangani pengecualian; daripada Anda memiliki izin saya untuk menangani pengecualian daripada menciptakan penjaga yang kompleks di mana-mana.


sumber
Add'l point: Jika Pengecualian Anda memberikan informasi penangkapan kegagalan (pengambil seperti "Param getWhatParamMessedMeUp ()"), itu dapat membantu pengguna API Anda membuat keputusan yang baik tentang apa yang harus dilakukan selanjutnya. Jika tidak, Anda hanya memberikan nama ke status kesalahan.
jasonnerothin
2

Jika Anda menggunakan penangan pengecualian untuk aliran kontrol, Anda terlalu umum dan malas. Seperti yang disebutkan orang lain, Anda tahu sesuatu terjadi jika Anda menangani pemrosesan di handler, tetapi apa sebenarnya? Pada dasarnya Anda menggunakan pengecualian untuk pernyataan lain, jika Anda menggunakannya untuk aliran kontrol.

Jika Anda tidak tahu keadaan apa yang mungkin terjadi, maka Anda dapat menggunakan penangan pengecualian untuk keadaan tidak terduga, misalnya ketika Anda harus menggunakan perpustakaan pihak ketiga, atau Anda harus menangkap semua yang ada di UI untuk menunjukkan kesalahan yang bagus. pesan dan catat pengecualiannya.

Namun, jika Anda tahu apa yang salah, dan Anda tidak memberikan pernyataan if atau sesuatu untuk memeriksanya, maka Anda hanya malas. Membiarkan handler pengecualian menjadi catch-all untuk hal-hal yang Anda tahu bisa terjadi adalah malas, dan itu akan kembali menghantui Anda nanti, karena Anda akan mencoba untuk memperbaiki situasi di handler pengecualian Anda berdasarkan asumsi yang mungkin salah.

Jika Anda memasukkan logika ke pengendali pengecualian untuk menentukan apa yang sebenarnya terjadi, maka Anda akan sangat bodoh karena tidak memasukkan logika itu ke dalam blok percobaan.

Penangan pengecualian adalah pilihan terakhir, karena ketika Anda kehabisan ide / cara untuk menghentikan sesuatu dari kesalahan, atau hal-hal di luar kemampuan Anda untuk mengendalikan. Seperti, server down dan time out dan Anda tidak dapat mencegah pengecualian itu terlempar.

Akhirnya, semua pemeriksaan dilakukan di muka menunjukkan apa yang Anda ketahui atau harapkan akan terjadi dan membuatnya eksplisit. Kode harus jelas maksudnya. Apa yang ingin Anda baca?


sumber
1
Tidak benar sama sekali: "Pada dasarnya Anda menggunakan pengecualian untuk pernyataan lain, jika Anda menggunakannya untuk aliran kontrol." Jika Anda menggunakannya untuk aliran kontrol, Anda tahu apa yang Anda tangkap dengan tepat dan tidak pernah menggunakan tangkapan umum, tetapi yang spesifik tentu saja!
Peter
2

Anda mungkin tertarik untuk melihat sistem kondisi Common Lisp yang merupakan semacam generalisasi pengecualian yang dilakukan dengan benar. Karena Anda dapat melepas tumpukan atau tidak dengan cara yang terkendali, Anda mendapatkan "restart" juga, yang sangat berguna.

Ini tidak ada hubungannya dengan praktik terbaik dalam bahasa lain, tetapi ini menunjukkan kepada Anda apa yang dapat dilakukan dengan beberapa pemikiran desain dalam (kira-kira) arah yang Anda pikirkan.

Tentu saja masih ada pertimbangan kinerja jika Anda memantul ke atas dan ke bawah tumpukan seperti yo-yo, tetapi itu adalah ide yang jauh lebih umum daripada pendekatan "oh crap, lets bail" yang paling banyak diwujudkan oleh sistem penangkapan catch / throw.

simon
sumber
2

Saya tidak berpikir ada yang salah dengan menggunakan Pengecualian untuk kontrol aliran. Pengecualian agak mirip dengan kelanjutan dan dalam bahasa yang diketik secara statis, Pengecualian lebih kuat dari kelanjutan, jadi, jika Anda membutuhkan kelanjutan tetapi bahasa Anda tidak memilikinya, Anda dapat menggunakan Pengecualian untuk mengimplementasikannya.

Sebenarnya, jika Anda membutuhkan kelanjutan dan bahasa Anda tidak memilikinya, Anda memilih bahasa yang salah dan Anda harus menggunakan yang berbeda. Tapi kadang-kadang Anda tidak punya pilihan: pemrograman web sisi klien adalah yang contoh utama - tidak hanya ada cara untuk mendapatkan sekitar JavaScript.

Contoh: Microsoft Volta adalah proyek yang memungkinkan penulisan aplikasi web dalam .NET, dan membiarkan kerangka kerja mencari tahu bit mana yang perlu dijalankan di mana. Salah satu konsekuensi dari ini adalah bahwa Volta harus dapat mengkompilasi CIL ke JavaScript, sehingga Anda dapat menjalankan kode pada klien. Namun, ada masalah: .NET memiliki multithreading, JavaScript tidak. Jadi, Volta mengimplementasikan kelanjutan dalam JavaScript menggunakan Pengecualian JavaScript, lalu mengimplementasikan .NET Threads menggunakan kelanjutan tersebut. Dengan begitu, aplikasi Volta yang menggunakan utas dapat dikompilasi untuk dijalankan di peramban yang tidak dimodifikasi - tidak diperlukan Silverlight.

Jörg W Mittag
sumber
2

Salah satu alasan estetika:

Mencoba selalu disertai dengan tangkapan, sedangkan jika tidak harus datang dengan yang lain.

if (PerformCheckSucceeded())
   DoSomething();

Dengan mencoba / menangkap, itu menjadi jauh lebih bertele-tele.

try
{
   PerformCheckSucceeded();
   DoSomething();
}
catch
{
}

Itu 6 baris kode terlalu banyak.

gzak
sumber
1

Tetapi Anda tidak akan selalu tahu apa yang terjadi dalam Metode yang Anda panggil. Anda tidak akan tahu persis di mana pengecualian itu dilemparkan. Tanpa memeriksa objek pengecualian secara lebih rinci ....

Mesh
sumber
1

Saya merasa tidak ada yang salah dengan teladan Anda. Sebaliknya, adalah dosa untuk mengabaikan pengecualian yang dilemparkan oleh fungsi yang dipanggil.

Dalam JVM, melempar pengecualian tidak terlalu mahal, hanya membuat pengecualian dengan xyzException baru (...), karena yang terakhir melibatkan stack walk. Jadi, jika Anda memiliki beberapa pengecualian yang dibuat sebelumnya, Anda dapat melemparkannya berkali-kali tanpa biaya. Tentu saja, dengan cara ini Anda tidak bisa melewatkan data bersama dengan pengecualian, tapi saya pikir itu adalah hal yang buruk untuk dilakukan.

Ingo
sumber
Maaf, ini salah besar, Brann. Itu tergantung kondisinya. Tidak selalu demikian kondisinya sepele. Dengan demikian, pernyataan if bisa memakan waktu berjam-jam, atau berhari-hari atau bahkan lebih lama.
Ingo
Dalam JVM, itu. Tidak lebih mahal dari pengembalian. Sosok pergi. Tetapi pertanyaannya adalah, apa yang akan Anda tulis dalam pernyataan if, jika bukan kode yang sudah ada dalam fungsi yang dipanggil untuk memberi tahu kasus luar biasa dari yang normal --- dengan demikian duplikasi kode.
Ingo
1
Ingo: situasi luar biasa adalah situasi yang tidak Anda harapkan. yaitu salah satu yang belum Anda pikirkan sebagai programmer. Jadi aturan saya adalah "menulis kode yang tidak membuang pengecualian" :)
Brann
1
Saya tidak pernah menulis penangan pengecualian, saya selalu memperbaiki masalah (kecuali ketika saya tidak bisa melakukan itu karena saya tidak memiliki kendali atas kode kesalahan). Dan saya tidak pernah melempar pengecualian, kecuali ketika kode yang saya tulis ditujukan untuk digunakan orang lain (misalnya perpustakaan). Tidak tunjukkan kontradiksinya kepada saya?
Brann
1
Saya setuju dengan Anda untuk tidak melempar pengecualian dengan liar. Namun yang pasti, apa yang "luar biasa" adalah masalah definisi. String.parseDouble melempar pengecualian jika tidak dapat memberikan hasil yang bermanfaat, misalnya, dalam rangka. Apa lagi yang harus dilakukan? Kembali NaN? Bagaimana dengan perangkat keras non-IEEE?
Ingo
1

Ada beberapa mekanisme umum di mana bahasa dapat memungkinkan metode untuk keluar tanpa mengembalikan nilai dan melepas ke blok "tangkapan" berikutnya:

  • Mintalah metode memeriksa bingkai tumpukan untuk menentukan situs panggilan, dan menggunakan metadata untuk situs panggilan untuk menemukan informasi tentang tryblok dalam metode panggilan, atau lokasi di mana metode panggilan menyimpan alamat pemanggilnya; dalam situasi terakhir, periksa metadata untuk penelepon penelepon untuk menentukan dengan cara yang sama dengan penelepon langsung, ulangi sampai orang menemukan tryblok atau tumpukan kosong. Pendekatan ini menambahkan sangat sedikit overhead ke kasus tanpa pengecualian (itu menghalangi beberapa optimasi) tetapi mahal ketika pengecualian terjadi.

  • Mintalah metode mengembalikan bendera "tersembunyi" yang membedakan pengembalian normal dari pengecualian, dan minta penelepon memeriksa bendera dan cabang itu ke rutinitas "pengecualian" jika disetel. Rutin ini menambahkan 1-2 instruksi ke kasus tanpa pengecualian, tetapi relatif sedikit overhead ketika pengecualian terjadi.

  • Minta penelepon menempatkan informasi atau kode penanganan perkecualian pada alamat tetap relatif terhadap alamat pengembalian yang ditumpuk. Misalnya, dengan ARM, alih-alih menggunakan instruksi "BL subroutine", orang dapat menggunakan urutan:

        adr lr,next_instr
        b subroutine
        b handle_exception
    next_instr:
    

Untuk keluar secara normal, subrutin hanya akan melakukan bx lratau pop {pc}; dalam kasus keluar abnormal, subrutin akan mengurangi 4 dari LR sebelum melakukan pengembalian atau penggunaan sub lr,#4,pc(tergantung pada variasi ARM, mode eksekusi, dll.) Pendekatan ini akan rusak sangat buruk jika penelepon tidak dirancang untuk mengakomodasi itu.

Bahasa atau kerangka kerja yang menggunakan pengecualian yang dicentang mungkin mendapat manfaat dari memiliki mereka yang ditangani dengan mekanisme seperti # 2 atau # 3 di atas, sementara pengecualian yang tidak dicentang ditangani menggunakan # 1. Meskipun implementasi pengecualian yang diperiksa di Jawa agak mengganggu, mereka tidak akan menjadi konsep yang buruk jika ada sarana yang oleh situs panggilan dapat mengatakan, pada dasarnya, "Metode ini dinyatakan sebagai melempar XX, tapi saya tidak mengharapkannya pernah melakukannya; jika ya, rethrow sebagai pengecualian "tidak dicentang". Dalam kerangka di mana pengecualian diperiksa ditangani dengan cara seperti itu, mereka bisa menjadi cara yang efektif untuk kontrol aliran untuk hal-hal seperti metode parsing yang dalam beberapa konteks mungkin memiliki kemungkinan besar kegagalan, tetapi di mana kegagalan harus mengembalikan informasi yang secara fundamental berbeda dari kesuksesan. Namun saya tidak mengetahui adanya kerangka kerja yang menggunakan pola seperti itu. Sebaliknya, pola yang lebih umum adalah menggunakan pendekatan pertama di atas (biaya minimal untuk kasus tanpa pengecualian, tetapi biaya tinggi ketika pengecualian dilemparkan) untuk semua pengecualian.

supercat
sumber