Desain berdasarkan kontrak menggunakan pernyataan atau pengecualian? [Tutup]

123

Ketika pemrograman dengan kontrak, suatu fungsi atau metode pertama kali memeriksa apakah prasyaratnya terpenuhi, sebelum mulai mengerjakan tanggung jawabnya, bukan? Dua cara paling menonjol untuk melakukan pemeriksaan ini adalah by assertand by exception.

  1. menegaskan gagal hanya dalam mode debug. Untuk memastikan sangat penting untuk (unit) menguji semua prasyarat kontrak terpisah untuk melihat apakah mereka benar-benar gagal.
  2. pengecualian gagal dalam mode debug dan rilis. Keuntungannya adalah perilaku debug yang diuji identik dengan perilaku rilis, tetapi menimbulkan penalti performa waktu proses.

Mana yang menurut Anda lebih disukai?

Lihat pertanyaan yang dirilis di sini

andreas buykx
sumber
3
Inti utama di balik desain berdasarkan kontrak adalah Anda tidak perlu (dan mungkin tidak) memverifikasi prasyarat pada waktu proses. Anda memverifikasi masukan sebelum melewati ke metode dengan prasyarat, bahwa ini bagaimana Anda menghormati Anda akhir kontrak. Jika masukan tidak valid atau melanggar akhir kontrak Anda, program biasanya akan tetap gagal melalui tindakan normalnya (yang Anda inginkan).
void.pointer
Pertanyaan yang bagus, tetapi saya pikir Anda harus benar-benar mengganti jawaban yang diterima (seperti yang ditunjukkan oleh suara juga)!
DaveFar
Selamanya, saya tahu, tetapi haruskah pertanyaan ini benar-benar memiliki tag c ++? Saya mencari jawaban ini, untuk digunakan dalam bahasa lain (Delpih) dan saya tidak dapat membayangkan bahasa apa pun yang menampilkan pengecualian dan pernyataan yang tidak akan mengikuti aturan yang sama. (Masih mempelajari pedoman Stack Overflow.)
Eric G
Respons yang sangat singkat diberikan dalam respons ini : "Dengan kata lain, pengecualian menangani ketahanan aplikasi Anda sementara pernyataan menunjukkan kebenarannya."
Shmuel Levine

Jawaban:

39

Menonaktifkan assert dalam build rilis sama saja dengan mengatakan "Saya tidak akan pernah mengalami masalah apa pun dalam build rilis", yang seringkali tidak terjadi. Jadi, assert tidak boleh dinonaktifkan dalam build rilis. Tapi Anda juga tidak ingin rilis build mengalami error setiap kali terjadi error, bukan?

Jadi gunakan pengecualian dan gunakan dengan baik. Gunakan hierarki pengecualian yang baik dan solid dan pastikan bahwa Anda menangkap dan Anda dapat memasang kait pada pengecualian yang melempar debugger Anda untuk menangkapnya, dan dalam mode rilis Anda dapat mengkompensasi kesalahan daripada langsung crash. Ini cara yang lebih aman untuk pergi.

pohon coppro
sumber
4
Assertion berguna paling tidak dalam kasus di mana pemeriksaan kebenaran akan menjadi tidak efisien atau tidak efisien untuk diterapkan dengan benar.
Casebash
89
Inti dari pernyataan bukanlah untuk memperbaiki kesalahan, tetapi untuk mengingatkan programmer. Menjaga mereka yang aktif dalam rilis membangun tidak berguna untuk yang alasan: Apa yang akan Anda telah mendapatkan dengan memiliki tembak menegaskan? Pengembang tidak akan dapat masuk dan men-debugnya. Assertion adalah bantuan debugging, itu bukan pengganti untuk pengecualian (dan juga bukan pengecualian adalah pengganti untuk pernyataan). Pengecualian mengingatkan program pada kondisi kesalahan. Tegaskan pemberitahuan pengembang.
jalf
12
Tetapi pernyataan harus digunakan ketika data internal telah rusak setelah diperbaiki - jika pernyataan dipicu, Anda tidak dapat membuat asumsi tentang status program karena itu berarti ada sesuatu / salah /. Jika pernyataan sudah tidak aktif, Anda tidak dapat menganggap data apa pun valid. Itulah mengapa rilis build harus menegaskan - bukan untuk memberi tahu programmer di mana masalahnya, tetapi agar program dapat ditutup dan tidak mengambil risiko masalah yang lebih besar. Program harus melakukan apa yang dapat dilakukan untuk memfasilitasi pemulihan nanti, ketika data dapat dipercaya.
coppro
5
@jalf, Meskipun Anda tidak dapat menghubungkan debugger Anda dalam versi rilis, Anda dapat memanfaatkan logging sehingga pengembang melihat informasi yang relevan dengan pernyataan Anda gagal. Dalam dokumen ini ( martinfowler.com/ieeeSoftware/failFast.pdf ), Jim Shore menunjukkan, "Ingat, kesalahan yang terjadi di situs pelanggan berhasil melewati proses pengujian Anda. Anda mungkin akan kesulitan mereproduksinya. Kesalahan ini adalah yang paling sulit ditemukan, dan pernyataan yang tepat yang menjelaskan masalah tersebut dapat menghemat waktu Anda. "
StriplingWarrior
5
Secara pribadi saya lebih suka menegaskan untuk desain dengan pendekatan kontrak. Pengecualian bersifat defensif dan melakukan pemeriksaan argumen di dalam fungsi. Selain itu, prasyarat dbc tidak mengatakan "Saya tidak akan berfungsi jika Anda menggunakan nilai di luar rentang kerja" tetapi "Saya tidak menjamin untuk memberikan jawaban yang benar, tetapi saya masih dapat melakukannya". Asserts memberikan umpan balik kepada pengembang bahwa mereka memanggil fungsi dengan pelanggaran kondisi, tetapi tidak menghentikan mereka untuk menggunakannya jika mereka merasa lebih tahu. Pelanggaran dapat menyebabkan terjadinya pengecualian, tetapi saya melihatnya sebagai hal yang berbeda.
Matt_JD
194

Aturan praktisnya adalah Anda harus menggunakan pernyataan saat mencoba menangkap kesalahan Anda sendiri, dan pengecualian saat mencoba menangkap kesalahan orang lain. Dengan kata lain, Anda harus menggunakan pengecualian untuk memeriksa prasyarat untuk fungsi API publik, dan setiap kali Anda mendapatkan data yang ada di luar sistem Anda. Anda harus menggunakan asserts untuk fungsi atau data internal sistem Anda.

Dima
sumber
bagaimana dengan serialize / deserialize duduk di modul / aplikasi yang berbeda dan akhirnya tidak sinkron? Maksud saya di bagian pembaca, selalu menjadi kesalahan saya jika saya mencoba membaca hal-hal dengan cara yang salah sehingga saya cenderung menggunakan asserts, tetapi di sisi lain saya memiliki data eksternal, yang pada akhirnya dapat mengubah format tanpa pemberitahuan.
Slava
Jika datanya eksternal, maka Anda harus menggunakan pengecualian. Dalam kasus khusus ini Anda mungkin juga harus menangkap pengecualian tersebut, dan menanganinya dengan cara yang masuk akal, daripada membiarkan program Anda mati. Juga, jawaban saya adalah aturan praktis, bukan hukum alam. :) Jadi, Anda harus mempertimbangkan setiap kasus satu per satu.
Dima
Jika fungsi Anda f (int * x) berisi baris x-> len di dalamnya, maka f (v) di mana v terbukti null dijamin akan crash. Selain itu, jika sebelumnya pada v terbukti nol namun f (v) terbukti dipanggil, Anda memiliki kontradiksi logis. Ini sama dengan memiliki a / b di mana b pada akhirnya terbukti menjadi 0. Idealnya, kode seperti itu harus gagal untuk dikompilasi. Menonaktifkan pemeriksaan asumsi sepenuhnya bodoh kecuali masalahnya adalah biaya pemeriksaan, karena mengaburkan lokasi di mana asumsi dilanggar. Setidaknya harus dicatat. Anda harus memiliki desain restart-on-crash.
Rob
22

Prinsip yang saya ikuti adalah ini: Jika suatu situasi dapat dihindari secara realistis dengan pengkodean, maka gunakan pernyataan. Jika tidak, gunakan pengecualian.

Pernyataan adalah untuk memastikan bahwa Kontrak dipatuhi. Kontrak harus adil, sehingga klien harus berada dalam posisi untuk memastikan kepatuhannya. Misalnya, Anda dapat menyatakan dalam kontrak bahwa URL harus valid karena aturan tentang apa yang ada dan bukan URL yang valid diketahui dan konsisten.

Pengecualian berlaku untuk situasi yang berada di luar kendali klien dan server. Pengecualian berarti ada sesuatu yang tidak beres, dan tidak ada yang bisa dilakukan untuk menghindarinya. Misalnya, konektivitas jaringan berada di luar kendali aplikasi sehingga tidak ada yang dapat dilakukan untuk menghindari kesalahan jaringan.

Saya ingin menambahkan bahwa perbedaan Assertion / Exception sebenarnya bukanlah cara terbaik untuk memikirkannya. Apa yang benar-benar ingin Anda pikirkan adalah tentang kontrak dan bagaimana hal itu dapat diberlakukan. Dalam contoh URL saya di atas, hal terbaik yang harus dilakukan adalah memiliki kelas yang merangkum URL dan berupa Null atau URL yang valid. Ini adalah konversi string menjadi URL yang memberlakukan kontrak, dan pengecualian dilemparkan jika tidak valid. Metode dengan parameter URL jauh lebih jelas daripada metode dengan parameter String dan pernyataan yang menentukan URL.

Ged Byrne
sumber
6

Pernyataan adalah untuk menangkap kesalahan yang telah dilakukan pengembang (bukan hanya Anda sendiri - juga pengembang lain di tim Anda). Jika wajar jika kesalahan pengguna dapat membuat kondisi ini, maka itu harus menjadi pengecualian.

Pikirkan juga konsekuensinya. Sebuah assert biasanya mematikan aplikasi. Jika terdapat ekspektasi realistis bahwa kondisi tersebut dapat dipulihkan, Anda mungkin harus menggunakan pengecualian.

Di sisi lain, jika masalahnya hanya disebabkan oleh kesalahan programmer maka gunakan assert, karena Anda ingin mengetahuinya secepat mungkin. Pengecualian mungkin tertangkap dan ditangani, dan Anda tidak akan pernah mengetahuinya. Dan ya, Anda harus menonaktifkan pernyataan dalam kode rilis karena di sana Anda ingin aplikasi dipulihkan jika ada kemungkinan kecil. Bahkan jika status program Anda sangat rusak, pengguna mungkin dapat menyimpan pekerjaan mereka.

DJClayworth
sumber
5

Tidak sepenuhnya benar bahwa "assert hanya gagal dalam mode debug".

Dalam Konstruksi Perangkat Lunak Berorientasi Objek, Edisi ke-2 oleh Bertrand Meyer, penulis membiarkan pintu terbuka untuk memeriksa prasyarat dalam mode rilis. Dalam kasus tersebut, apa yang terjadi saat pernyataan gagal adalah bahwa ... pengecualian pelanggaran pernyataan dimunculkan! Dalam kasus ini, tidak ada pemulihan dari situasi tersebut: sesuatu yang berguna dapat dilakukan, dan itu adalah untuk secara otomatis menghasilkan laporan kesalahan dan, dalam beberapa kasus, untuk memulai ulang aplikasi.

Motivasi di balik ini adalah bahwa prasyarat biasanya lebih murah untuk diuji daripada invariant dan postconditions, dan bahwa dalam beberapa kasus kebenaran dan "keamanan" dalam rilis build lebih penting daripada kecepatan. yaitu Untuk banyak aplikasi, kecepatan bukanlah masalah, tetapi ketahanan (kemampuan program untuk berperilaku dengan cara yang aman ketika perilakunya tidak benar, yaitu ketika kontrak diputus).

Haruskah Anda selalu membiarkan pemeriksaan prasyarat diaktifkan? Tergantung. Terserah kamu. Tidak ada jawaban universal. Jika Anda membuat perangkat lunak untuk bank, mungkin lebih baik menghentikan eksekusi dengan pesan yang mengkhawatirkan daripada mentransfer $ 1.000.000 daripada $ 1.000. Tetapi bagaimana jika Anda memprogram game? Mungkin Anda membutuhkan semua kecepatan yang bisa Anda dapatkan, dan jika seseorang mendapat 1000 poin, bukan 10 karena bug yang tidak dapat ditangkap prasyaratnya (karena tidak diaktifkan), sial.

Dalam kedua kasus, Anda idealnya telah menangkap bug itu selama pengujian, dan Anda harus melakukan bagian penting dari pengujian Anda dengan pernyataan diaktifkan. Apa yang dibahas di sini adalah kebijakan apa yang terbaik untuk kasus yang jarang terjadi di mana prasyarat gagal dalam kode produksi dalam skenario yang tidak terdeteksi sebelumnya karena pengujian yang tidak lengkap.

Untuk meringkas, Anda dapat memiliki pernyataan dan masih mendapatkan pengecualian secara otomatis , jika Anda membiarkannya diaktifkan - setidaknya di Eiffel. Saya pikir untuk melakukan hal yang sama di C ++ Anda perlu mengetiknya sendiri.

Lihat juga: Kapan pernyataan harus tetap berada dalam kode produksi?

Daniel Daranas
sumber
1
Poin Anda pasti valid. SO tidak menentukan bahasa tertentu - dalam kasus C # pernyataan standarnya adalah System.Diagnostics.Debug.Assert, yang tidak hanya gagal dalam build Debug, dan akan dihapus pada waktu kompilasi dalam build Release.
yoyo
2

Ada benang yang sangat besar tentang pengaktifan / penonaktifan pernyataan dalam rilis build di comp.lang.c ++. Dimoderasi, yang jika Anda punya waktu beberapa minggu, Anda dapat melihat seberapa beragam pendapat tentang hal ini. :)

Berlawanan dengan coppro , saya percaya bahwa jika Anda tidak yakin bahwa sebuah pernyataan dapat dinonaktifkan dalam build rilis, maka pernyataan tersebut seharusnya tidak menjadi sebuah assert. Assertion adalah untuk melindungi dari kerusakan program invariants. Dalam kasus seperti itu, sejauh menyangkut klien kode Anda, akan ada salah satu dari dua kemungkinan hasil:

  1. Mati dengan beberapa jenis kegagalan jenis OS, mengakibatkan panggilan untuk dibatalkan. (Tanpa menegaskan)
  2. Mati melalui panggilan langsung untuk membatalkan. (Dengan menegaskan)

Tidak ada perbedaan bagi pengguna, namun, pernyataan tersebut mungkin saja menambahkan biaya performa yang tidak perlu dalam kode yang ada di sebagian besar proses yang kode tidak gagal.

Jawaban atas pertanyaan sebenarnya lebih bergantung pada siapa klien API nantinya. Jika Anda menulis perpustakaan yang menyediakan API, Anda memerlukan beberapa bentuk mekanisme untuk memberi tahu pelanggan Anda bahwa mereka telah menggunakan API secara tidak benar. Kecuali Anda menyediakan dua versi pustaka (satu dengan asserts, satu tanpa) maka assert sangat tidak mungkin merupakan pilihan yang tepat.

Secara pribadi, bagaimanapun, saya tidak yakin apakah saya akan pergi dengan pengecualian untuk kasus ini juga. Pengecualian lebih cocok untuk di mana bentuk pemulihan yang sesuai dapat dilakukan. Misalnya, Anda mungkin mencoba mengalokasikan memori. Saat Anda menemukan pengecualian 'std :: bad_alloc', Anda mungkin dapat mengosongkan memori dan mencoba lagi.

Richard Corden
sumber
2

Saya menguraikan pandangan saya tentang keadaan masalah di sini: Bagaimana Anda memvalidasi keadaan internal suatu objek? . Umumnya, tegaskan klaim Anda dan ajukan pelanggaran oleh orang lain. Untuk menonaktifkan assert dalam build rilis, Anda dapat melakukan:

  • Nonaktifkan pernyataan untuk pemeriksaan mahal (seperti memeriksa apakah suatu rentang dipesan)
  • Tetap aktifkan pemeriksaan sepele (seperti memeriksa pointer nol atau nilai boolean)

Tentu saja, dalam build rilis, assertion yang gagal dan pengecualian yang tidak tertangkap harus ditangani dengan cara lain selain dalam build debug (yang bisa dipanggil std :: abort). Tulis log kesalahan di suatu tempat (mungkin ke dalam file), beri tahu pelanggan bahwa terjadi kesalahan internal. Pelanggan akan dapat mengirimkan file log kepada Anda.

Johannes Schaub - litb
sumber
1

Anda bertanya tentang perbedaan antara kesalahan desain-waktu dan run-time.

menegaskan adalah pemberitahuan 'hai programmer, ini rusak', mereka ada di sana untuk mengingatkan Anda tentang bug yang tidak akan Anda perhatikan ketika terjadi.

pengecualian adalah pemberitahuan 'hai pengguna, ada yang salah' (jelas Anda dapat membuat kode untuk menangkapnya sehingga pengguna tidak pernah diberi tahu) tetapi ini dirancang untuk terjadi pada waktu proses ketika pengguna Joe menggunakan aplikasi.

Jadi, jika menurut Anda Anda bisa mengatasi semua bug, gunakan pengecualian saja. Jika Anda berpikir Anda tidak bisa ..... gunakan pengecualian. Anda masih bisa menggunakan pernyataan debug untuk mengurangi jumlah pengecualian tentunya.

Jangan lupa bahwa banyak prasyarat adalah data yang disediakan pengguna, jadi Anda memerlukan cara yang baik untuk memberi tahu pengguna bahwa datanya tidak baik. Untuk melakukan itu, Anda sering kali harus mengembalikan data kesalahan ke tumpukan panggilan ke bit yang berinteraksi dengannya. Asserts tidak akan berguna kemudian - jadi dua kali lipat jika aplikasi Anda n-tier.

Terakhir, saya tidak akan menggunakan keduanya - kode kesalahan jauh lebih unggul untuk kesalahan yang menurut Anda akan terjadi secara teratur. :)

gbjbaanb.dll
sumber
0

Saya lebih suka yang kedua. Meskipun pengujian Anda mungkin berjalan dengan baik, Murphy mengatakan bahwa sesuatu yang tidak terduga akan menjadi salah. Jadi, alih-alih mendapatkan pengecualian pada panggilan metode salah yang sebenarnya, Anda malah menelusuri NullPointerException (atau yang setara) 10 tumpukan frame lebih dalam.

jdmichal.dll
sumber
0

Jawaban sebelumnya benar: gunakan pengecualian untuk fungsi API publik. Satu-satunya saat Anda mungkin ingin membengkokkan aturan ini adalah saat cek tersebut mahal secara komputasi. Dalam hal ini, Anda dapat memasukkannya ke dalam assert.

Jika menurut Anda pelanggaran terhadap prasyarat tersebut kemungkinan besar terjadi, simpan sebagai pengecualian, atau perbaiki prasyarat tersebut.

Mike Elkins
sumber
0

Anda harus menggunakan keduanya. Asserts adalah untuk kenyamanan Anda sebagai pengembang. Pengecualian menangkap hal-hal yang Anda lewatkan atau tidak harapkan selama runtime.

Saya semakin menyukai fungsi pelaporan kesalahan glib alih-alih pernyataan lama yang biasa. Mereka berperilaku seperti pernyataan assert tetapi bukannya menghentikan program, mereka hanya mengembalikan nilai dan membiarkan program berlanjut. Ini bekerja dengan sangat baik, dan sebagai bonus Anda bisa melihat apa yang terjadi pada program Anda lainnya ketika suatu fungsi tidak mengembalikan "apa yang seharusnya". Jika macet, Anda tahu bahwa pemeriksaan kesalahan Anda longgar di tempat lain di jalan.

Dalam proyek terakhir saya, saya menggunakan gaya fungsi ini untuk mengimplementasikan pemeriksaan prekondisi, dan jika salah satunya gagal, saya akan mencetak jejak tumpukan ke file log tetapi terus berjalan. Menghemat banyak waktu debugging saya ketika orang lain menghadapi masalah saat menjalankan build debug saya.

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

Jika saya memerlukan pemeriksaan runtime argumen, saya akan melakukan ini:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}
individu
sumber
Saya rasa saya tidak pernah melihat dalam pertanyaan OP apapun yang berhubungan dengan C ++. Saya yakin itu tidak seharusnya dimasukkan dalam jawaban Anda.
ForceMagic
@ForceMagic: Pertanyaan tersebut memiliki tag C ++ pada tahun 2008 ketika saya memposting jawaban ini, dan sebenarnya tag C ++ telah dihapus hanya 5 jam yang lalu. Terlepas dari itu, kode tersebut menggambarkan konsep bahasa-independen.
indiv
0

Saya mencoba mensintesis beberapa jawaban lain di sini dengan pandangan saya sendiri.

Gunakan pernyataan untuk kasus di mana Anda ingin menonaktifkannya dalam produksi, keliru membiarkannya masuk. Satu-satunya alasan sebenarnya untuk menonaktifkan dalam produksi, tetapi tidak dalam pengembangan, adalah untuk mempercepat program. Dalam kebanyakan kasus, percepatan ini tidak akan signifikan, tetapi terkadang kode sangat membutuhkan waktu atau pengujian mahal secara komputasi. Jika kode sangat penting untuk misi, maka pengecualian mungkin yang terbaik meskipun terjadi perlambatan.

Jika ada peluang nyata untuk pulih, gunakan pengecualian karena pernyataan tidak dirancang untuk dipulihkan. Misalnya, kode jarang dirancang untuk memulihkan kesalahan pemrograman, tetapi dirancang untuk memulihkan dari faktor-faktor seperti kegagalan jaringan atau file yang terkunci. Kesalahan tidak boleh ditangani sebagai pengecualian hanya karena berada di luar kendali programmer. Sebaliknya, prediktabilitas kesalahan ini, dibandingkan dengan kesalahan pengkodean, membuatnya lebih ramah untuk pemulihan.

Argumen ulang bahwa lebih mudah untuk men-debug pernyataan: Jejak tumpukan dari pengecualian bernama benar semudah membaca pernyataan. Kode yang baik seharusnya hanya menangkap jenis pengecualian tertentu, jadi pengecualian tidak boleh luput dari perhatian karena tertangkap. Namun, menurut saya Java terkadang memaksa Anda untuk menangkap semua pengecualian.

Casebash
sumber
0

Aturan praktisnya, bagi saya, adalah menggunakan ekspresi assert untuk menemukan kesalahan internal dan pengecualian untuk kesalahan eksternal. Anda bisa mendapatkan banyak keuntungan dari diskusi berikut oleh Greg dari sini .

Ekspresi assert digunakan untuk menemukan kesalahan pemrograman: baik kesalahan dalam logika program itu sendiri atau kesalahan dalam implementasi yang sesuai. Kondisi assert memverifikasi bahwa program tetap dalam keadaan yang ditentukan. "Keadaan yang ditentukan" pada dasarnya adalah salah satu yang setuju dengan asumsi program. Perhatikan bahwa "keadaan yang ditentukan" untuk sebuah program tidak perlu menjadi "keadaan ideal" atau bahkan "keadaan biasa", atau bahkan "keadaan berguna" tetapi lebih pada poin penting itu nanti.

Untuk memahami bagaimana pernyataan cocok ke dalam program, pertimbangkan rutinitas dalam program C ++ yang akan merujuk ke pointer. Sekarang haruskah tes rutin apakah pointer adalah NULL sebelum dereferencing, atau haruskah itu menegaskan bahwa pointer bukan NULL dan kemudian melanjutkan dan dereferensi itu terlepas?

Saya membayangkan bahwa kebanyakan pengembang ingin melakukan keduanya, menambahkan assert, tetapi juga memeriksa pointer untuk nilai NULL, agar tidak crash jika kondisi yang ditegaskan gagal. Di permukaan, melakukan tes dan pengecekan mungkin tampak sebagai keputusan yang paling bijaksana

Tidak seperti kondisi yang ditetapkan, penanganan kesalahan program (pengecualian) tidak mengacu pada kesalahan dalam program, tetapi pada input yang diperoleh program dari lingkungannya. Ini sering kali merupakan "kesalahan" di pihak seseorang, seperti pengguna mencoba masuk ke akun tanpa mengetikkan sandi. Dan meskipun kesalahan dapat menghalangi keberhasilan tugas program, tidak ada kegagalan program. Program gagal untuk masuk pengguna tanpa kata sandi karena kesalahan eksternal - kesalahan di pihak pengguna. Jika situasinya berbeda, dan pengguna mengetikkan kata sandi yang benar dan program gagal mengenalinya; kemudian meskipun hasilnya akan tetap sama, kegagalan tersebut sekarang menjadi milik program.

Tujuan penanganan kesalahan (pengecualian) ada dua. Yang pertama adalah untuk mengkomunikasikan kepada pengguna (atau klien lain) bahwa kesalahan dalam input program telah terdeteksi dan artinya. Tujuan kedua adalah memulihkan aplikasi setelah kesalahan terdeteksi, ke keadaan yang terdefinisi dengan baik. Perhatikan bahwa program itu sendiri tidak salah dalam situasi ini. Memang, program mungkin dalam keadaan tidak ideal, atau bahkan keadaan di mana tidak dapat melakukan apa pun yang berguna, tetapi tidak ada kesalahan pemrograman. Sebaliknya, karena status pemulihan kesalahan adalah salah satu yang diantisipasi oleh desain program, itu adalah salah satu yang dapat ditangani oleh program.

PS: Anda mungkin ingin memeriksa pertanyaan serupa: Exception Vs Assertion .

herohuyongtao
sumber
-1

Lihat juga pertanyaan ini :

Dalam beberapa kasus, pernyataan dinonaktifkan saat membuat untuk rilis. Anda mungkin tidak memiliki kendali atas ini (jika tidak, Anda dapat membangun dengan asserts on), jadi mungkin ide yang baik untuk melakukannya seperti ini.

Masalah dengan "mengoreksi" nilai input adalah bahwa pemanggil tidak akan mendapatkan apa yang mereka harapkan, dan ini dapat menyebabkan masalah atau bahkan crash di bagian program yang sama sekali berbeda, membuat debugging menjadi mimpi buruk.

Saya biasanya melempar pengecualian dalam pernyataan-if untuk mengambil alih peran pernyataan jika mereka dinonaktifkan

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
Rik
sumber