Saya telah menjadi insinyur perangkat lunak profesional selama sekitar satu tahun sekarang, setelah lulus dengan gelar CS. Saya sudah tahu tentang pernyataan untuk sementara waktu di C ++ dan C, tetapi tidak tahu mereka ada di C # dan. NET sama sekali sampai saat ini.
Kode produksi kami tidak berisi konfirmasi apa pun dan pertanyaan saya adalah ini ...
Haruskah saya mulai menggunakan Sisipan dalam kode produksi kami? Dan jika demikian, Kapan penggunaannya paling tepat? Apakah lebih masuk akal untuk dilakukan
Debug.Assert(val != null);
atau
if ( val == null )
throw new exception();
language-agnostic
exception
testing
assertions
defensive-programming
Nicholas Mancuso
sumber
sumber
Jawaban:
Dalam Aplikasi Debugging Microsoft .NET 2.0 John Robbins memiliki bagian besar tentang pernyataan. Poin utamanya adalah:
PS: Jika Anda menyukai Kode Lengkap, saya sarankan untuk melanjutkannya dengan buku ini. Saya membelinya untuk mempelajari tentang cara menggunakan WinDBG dan membuang file, tetapi bagian pertama dikemas dengan tips untuk membantu menghindari bug.
sumber
Debug.Assert
vsTrace.Assert
. Yang terakhir dieksekusi dalam rilis Rilis serta membangun Debug.Letakkan
Debug.Assert()
di mana-mana dalam kode di mana Anda ingin melakukan pemeriksaan kewarasan untuk memastikan invarian. Ketika Anda mengkompilasi rilis Rilis (yaitu, tidak adaDEBUG
kompiler konstan), panggilan untukDebug.Assert()
akan dihapus sehingga mereka tidak akan mempengaruhi kinerja.Anda masih harus membuang pengecualian sebelum menelepon
Debug.Assert()
. Pernyataan hanya memastikan bahwa semuanya seperti yang diharapkan saat Anda masih berkembang.sumber
Dari Kode Selesai
sumber
FWIW ... Saya menemukan bahwa metode publik saya cenderung menggunakan
if () { throw; }
pola untuk memastikan bahwa metode tersebut dipanggil dengan benar. Metode pribadi saya cenderung digunakanDebug.Assert()
.Idenya adalah bahwa dengan metode pribadi saya, saya yang di bawah kendali, jadi jika saya mulai memanggil salah satu metode pribadi saya dengan parameter yang salah, maka saya telah melanggar asumsi saya sendiri di suatu tempat - saya seharusnya tidak pernah mendapatkan ke dalam kondisi itu. Dalam produksi, pernyataan pribadi ini idealnya adalah pekerjaan yang tidak perlu karena saya seharusnya menjaga keadaan internal saya valid dan konsisten. Kontras dengan parameter yang diberikan kepada metode publik, yang dapat dipanggil oleh siapa saja pada saat runtime: Saya masih perlu menegakkan batasan parameter di sana dengan melemparkan pengecualian.
Selain itu, metode pribadi saya masih bisa melempar pengecualian jika sesuatu tidak berfungsi saat runtime (kesalahan jaringan, kesalahan akses data, data buruk diambil dari layanan pihak ketiga, dll.). Penegasan saya ada di sana untuk memastikan bahwa saya belum melanggar asumsi internal saya sendiri tentang keadaan objek.
sumber
Gunakan konfirmasi untuk memeriksa asumsi dan pengecualian pengembang untuk memeriksa asumsi lingkungan.
sumber
Jika aku jadi kamu, aku akan lakukan:
Atau untuk menghindari pemeriksaan kondisi berulang
sumber
Jika Anda ingin Menyertakan dalam kode produksi Anda (yaitu Rilis build), Anda dapat menggunakan Trace.Assert bukan Debug.Assert.
Ini tentu saja menambah biaya overhead untuk eksekusi produksi Anda.
Juga jika aplikasi Anda berjalan dalam mode antarmuka pengguna, dialog Penegasan akan ditampilkan secara default, yang mungkin sedikit membingungkan bagi pengguna Anda.
Anda bisa mengganti perilaku ini dengan menghapus DefaultTraceListener: lihat dokumentasi untuk Trace.Listeners di MSDN.
Singkatnya,
Gunakan Debug.Assert secara bebas untuk membantu menangkap bug di Debug builds.
Jika Anda menggunakan Trace.Assert dalam mode antarmuka pengguna, Anda mungkin ingin menghapus DefaultTraceListener untuk menghindari membingungkan pengguna.
Jika kondisi yang Anda uji adalah sesuatu yang tidak bisa ditangani oleh aplikasi Anda, Anda mungkin lebih baik melemparkan pengecualian, untuk memastikan eksekusi tidak berlanjut. Sadarilah bahwa pengguna dapat memilih untuk mengabaikan pernyataan.
sumber
Asserts digunakan untuk menangkap kesalahan programmer (Anda), bukan kesalahan pengguna. Mereka harus digunakan hanya ketika tidak ada kesempatan pengguna dapat menyebabkan pernyataan tersebut menyala. Jika Anda menulis API, misalnya, pernyataan tidak boleh digunakan untuk memeriksa bahwa argumen tidak nol dalam metode apa pun yang dapat dipanggil oleh pengguna API. Tetapi itu dapat digunakan dalam metode pribadi yang tidak diekspos sebagai bagian dari API Anda untuk menyatakan bahwa kode ANDA tidak pernah melewati argumen nol ketika seharusnya tidak.
Saya biasanya lebih menyukai pengecualian daripada menegaskan ketika saya tidak yakin.
sumber
Pendeknya
Asserts
digunakan untuk penjaga dan untuk memeriksa Desain dengan batasan Kontrak, yaitu:Asserts
seharusnya hanya untuk Debug dan non-Produksi. Asserts biasanya diabaikan oleh kompiler di rilis build.Asserts
dapat memeriksa bug / kondisi tak terduga yang ADA dalam kendali sistem AndaAsserts
BUKAN suatu mekanisme untuk validasi lini pertama input pengguna atau aturan bisnisAsserts
seharusnya tidak digunakan untuk mendeteksi kondisi lingkungan tak terduga (yang berada di luar kendali kode) misalnya dari memori, kegagalan jaringan, kegagalan database, dll Meskipun jarang, kondisi ini diharapkan (dan kode aplikasi Anda tidak dapat memperbaiki masalah seperti kegagalan perangkat keras atau kehabisan sumber daya). Biasanya, pengecualian akan dilemparkan - aplikasi Anda kemudian dapat mengambil tindakan korektif (misalnya coba lagi operasi database atau jaringan, upaya untuk membebaskan memori yang di-cache), atau batalkan dengan anggun jika pengecualian tidak dapat ditangani.Asserts
- kode Anda beroperasi di wilayah yang tidak terduga. Stack Traces dan crash dumps dapat digunakan untuk menentukan apa yang salah.Pernyataan memiliki manfaat yang sangat besar:
Debug
build.... Lebih detail
Debug.Assert
menyatakan suatu kondisi yang telah diasumsikan tentang status oleh sisa dari blok kode dalam kendali program. Ini dapat mencakup status parameter yang disediakan, status anggota instance kelas, atau bahwa pengembalian dari pemanggilan metode ada dalam rentang kontrak / desainnya. Biasanya, pernyataan harus menabrak utas / proses / program dengan semua informasi yang diperlukan (Stack Trace, Crash Dump, dll), karena mereka mengindikasikan adanya bug atau kondisi tidak dipertimbangkan yang belum dirancang untuk (yaitu jangan mencoba dan menangkap atau menangani kegagalan pernyataan), dengan satu pengecualian yang memungkinkan ketika pernyataan itu sendiri dapat menyebabkan lebih banyak kerusakan daripada bug (mis. Pengendali Lalu Lintas Udara tidak akan menginginkan YSOD ketika pesawat terbang menggunakan kapal selam, meskipun masih diperdebatkan apakah membangun debug harus digunakan untuk produksi ...)Kapan Anda harus menggunakan
Asserts?
- Pada titik mana saja dalam suatu sistem, atau pustaka API, atau layanan di mana input ke fungsi atau keadaan kelas diasumsikan valid (mis. Ketika validasi telah dilakukan pada input pengguna dalam tingkat presentasi dari suatu sistem , kelas bisnis dan kelas data mengasumsikan bahwa pemeriksaan nol, pemeriksaan rentang, pemeriksaan panjang string, dll pada input telah dilakukan). -Assert
Pemeriksaan umum mencakup di mana asumsi yang tidak valid akan menghasilkan dereferensi objek nol, pembagi nol, aritmatika numerik atau tanggal meluap, dan keluar umum dari band / tidak dirancang untuk perilaku (misalnya jika int 32 bit digunakan untuk memodelkan usia manusia , akan lebih bijaksana untukAssert
bahwa usia sebenarnya antara 0 dan 125 atau lebih - nilai -100 dan 10 ^ 10 tidak dirancang untuk).Kontrak Kode Net.
Di Stack Net, Kontrak Kode dapat digunakan sebagai tambahan, atau sebagai alternatif untuk menggunakan
Debug.Assert
. Kontrak Kode lebih lanjut dapat memformalkan pemeriksaan negara, dan dapat membantu mendeteksi pelanggaran asumsi pada ~ waktu kompilasi (atau segera sesudahnya, jika dijalankan sebagai pemeriksaan latar belakang dalam IDE).Pemeriksaan Desain dengan Kontrak (DBC) yang tersedia meliputi:
Contract.Requires
- Prasyarat yang DikontrakContract.Ensures
- Kontrak Pasca KontrakInvariant
- Mengekspresikan asumsi tentang keadaan suatu objek di semua titik dalam masa pakainya.Contract.Assumes
- menenangkan pemeriksa statis ketika panggilan ke metode dihiasi non-Kontrak dilakukan.sumber
Sebagian besar tidak pernah ada di buku saya. Dalam sebagian besar kesempatan jika Anda ingin memeriksa apakah semuanya waras maka buanglah jika tidak.
Apa yang saya tidak suka adalah kenyataan bahwa itu membuat fungsi debug berbeda secara fungsional dengan rilis build. Jika pernyataan debug gagal tetapi fungsi berfungsi dalam rilis lalu bagaimana itu masuk akal? Itu lebih baik ketika asserter telah lama meninggalkan perusahaan dan tidak ada yang tahu bagian dari kode itu. Maka Anda harus menghabiskan waktu Anda menjelajahi masalah untuk melihat apakah itu benar-benar masalah atau tidak. Jika itu masalah maka mengapa orang itu tidak melempar?
Bagi saya ini menyarankan dengan menggunakan Debug. Meminta Anda menunda masalah kepada orang lain, atasi sendiri masalahnya. Jika sesuatu yang seharusnya menjadi kasus dan tidak kemudian dibuang.
Saya kira mungkin ada skenario kritis kinerja di mana Anda ingin mengoptimalkan pernyataan Anda dan mereka berguna di sana, namun saya belum menemukan skenario seperti itu.
sumber
System.Diagnostics.Trace.Assert()
dijalankan dalam rilis Rilis serta membangun Debug.Menurut Standar IDesign , Anda harus
Sebagai penafian saya harus menyebutkan bahwa saya belum merasa praktis untuk menerapkan IRL ini. Tapi ini standar mereka.
sumber
Gunakan pernyataan hanya dalam kasus di mana Anda ingin cek dihapus untuk build rilis. Ingat, pernyataan Anda tidak akan aktif jika Anda tidak mengompilasi dalam mode debug.
Diberikan contoh check-for-null Anda, jika ini hanya API internal, saya mungkin menggunakan pernyataan. Jika itu dalam API publik, saya pasti akan menggunakan pemeriksaan dan lemparan eksplisit.
sumber
System.Diagnostics.Trace.Assert()
untuk menjalankan pernyataan dalam rilis (produksi) build.null
ketika: "Metode yang terlihat secara eksternal mendereferensi salah satu argumen referensi tanpa memverifikasi apakah argumen itu nol ." Dalam situasi seperti itu, metode atau properti harus dibuangArgumentNullException
.Semua pernyataan harus berupa kode yang dapat dioptimalkan untuk:
Karena memeriksa sesuatu yang Anda anggap benar. Misalnya:
Di atas, ada tiga pendekatan berbeda untuk parameter nol. Yang pertama menerimanya sebagai diizinkan (hanya tidak melakukan apa-apa). Yang kedua melemparkan pengecualian untuk menangani kode panggilan (atau tidak, menghasilkan pesan kesalahan). Yang ketiga menganggap itu tidak mungkin terjadi, dan menegaskan bahwa memang demikian.
Dalam kasus pertama, tidak ada masalah.
Dalam kasus kedua, ada masalah dengan kode panggilan - itu seharusnya tidak dipanggil
GetFirstAndConsume
dengan nol, sehingga mendapat pengecualian kembali.Dalam kasus ketiga, ada masalah dengan kode ini, karena seharusnya sudah diperiksa
en != null
sebelum dipanggil, sehingga itu tidak benar adalah bug. Atau dengan kata lain, itu harus kode yang secara teoritis dapat dioptimalkanDebug.Assert(true)
, sicneen != null
harus selalutrue
!sumber
en == null
dalam produksi? Apakah Anda mungkin mengatakan bahwa hal itu tidaken == null
akan pernah terjadi dalam produksi (karena program ini telah sepenuhnya di-debug)? Jika demikian, makaDebug.Assert(en != null)
paling tidak berfungsi sebagai alternatif untuk komentar. Tentu saja, jika perubahan di masa depan dibuat, itu juga terus memiliki nilai untuk mendeteksi kemungkinan kemunduran.Debug.Assert()
dihapus dalam versi Rilis. Jadi, jika Anda berada salah, dalam kasus ketiga , Anda tidak akan tahu itu dalam produksi (dengan asumsi penggunaan Release membangun dalam produksi). Namun, perilaku kasus pertama dan kedua identik dalam build Debug dan Release.Saya pikir saya akan menambahkan empat kasus lagi, di mana Debug. Assert dapat menjadi pilihan yang tepat.
1) Sesuatu yang belum saya lihat disebutkan di sini adalah cakupan konseptual tambahan yang dapat diberikan oleh Asserts selama pengujian otomatis . Sebagai contoh sederhana:
Ketika beberapa pemanggil tingkat yang lebih tinggi dimodifikasi oleh seorang penulis yang percaya mereka telah memperluas cakupan kode untuk menangani skenario tambahan, idealnya (!) Mereka akan menulis unit test untuk mencakup kondisi baru ini. Maka mungkin kode terintegrasi sepenuhnya tampaknya berfungsi dengan baik.
Namun, sebenarnya cacat yang halus telah diperkenalkan, tetapi tidak terdeteksi dalam hasil tes. Callee telah menjadi non-deterministik dalam kasus ini, dan hanya terjadi untuk memberikan hasil yang diharapkan. Atau mungkin telah menghasilkan kesalahan pembulatan yang tanpa disadari. Atau menyebabkan kesalahan yang diimbangi secara merata di tempat lain. Atau diberikan tidak hanya akses yang diminta tetapi juga hak istimewa tambahan yang tidak seharusnya diberikan. Dll
Pada titik ini, pernyataan Debug.Assert () yang terkandung dalam callee digabungkan dengan kasing baru (atau kasing tepi) yang didorong oleh unit test dapat memberikan notifikasi yang tak ternilai selama pengujian bahwa asumsi penulis asli telah dibatalkan, dan kode seharusnya tidak dirilis tanpa ulasan tambahan. Pertanyaan dengan unit test adalah mitra yang sempurna.
2) Selain itu, beberapa tes sederhana untuk ditulis, tetapi mahal dan tidak perlu diberikan asumsi awal . Sebagai contoh:
Jika suatu objek hanya dapat diakses dari titik masuk aman tertentu, haruskah permintaan tambahan dibuat ke database hak jaringan dari setiap metode objek untuk memastikan pemanggil memiliki izin? Tentunya tidak. Mungkin solusi ideal termasuk caching atau perluasan fitur lainnya, tetapi desain tidak memerlukannya. Debug.Assert () akan segera menunjukkan ketika objek telah dilampirkan ke titik masuk yang tidak aman.
3) Selanjutnya, dalam beberapa kasus, produk Anda mungkin tidak memiliki interaksi diagnostik yang membantu untuk semua atau sebagian operasinya ketika digunakan dalam mode rilis . Sebagai contoh:
Misalkan itu adalah perangkat real-time tertanam. Melempar pengecualian dan memulai kembali ketika menemukan paket yang salah adalah kontra-produktif. Alih-alih, perangkat mungkin mendapat manfaat dari pengoperasian dengan upaya terbaik, bahkan hingga menghasilkan derau dalam outputnya. Ini juga mungkin tidak memiliki antarmuka manusia, perangkat logging, atau bahkan dapat diakses secara fisik oleh manusia sama sekali ketika digunakan dalam mode rilis, dan kesadaran kesalahan paling baik diberikan dengan menilai output yang sama. Dalam hal ini, Pernyataan liberal dan pengujian pra-rilis menyeluruh lebih berharga daripada pengecualian.
4) Terakhir, beberapa tes tidak perlu hanya karena callee dianggap sangat dapat diandalkan . Dalam kebanyakan kasus, semakin banyak kode yang dapat digunakan kembali, semakin banyak upaya yang dilakukan agar dapat diandalkan. Oleh karena itu biasa untuk Pengecualian untuk parameter tak terduga dari penelepon, tetapi Tegas untuk hasil yang tidak terduga dari callees. Sebagai contoh:
Jika
String.Find
operasi inti menyatakan itu akan mengembalikan a-1
ketika kriteria pencarian tidak ditemukan, Anda mungkin dapat dengan aman melakukan satu operasi daripada tiga. Namun, jika benar-benar dikembalikan-2
, Anda mungkin tidak memiliki tindakan yang wajar. Akan tidak membantu untuk mengganti perhitungan yang lebih sederhana dengan perhitungan yang menguji nilai secara terpisah-1
, dan tidak masuk akal di sebagian besar lingkungan rilis untuk mengotori kode Anda dengan tes yang memastikan perpustakaan inti beroperasi seperti yang diharapkan. Dalam hal ini, Sisipan ideal.sumber
Kutipan Diambil dari Programmer Pragmatis: Dari Journeyman hingga Master
sumber
Anda harus selalu menggunakan pendekatan kedua (melempar pengecualian).
Juga jika Anda dalam produksi (dan memiliki rilis-build), lebih baik untuk melemparkan pengecualian (dan membiarkan aplikasi crash dalam kasus terburuk) daripada bekerja dengan nilai-nilai yang tidak valid dan mungkin menghancurkan data pelanggan Anda (yang mungkin berharga ribuan dolar).
sumber
Anda harus menggunakan Debug.Assert untuk menguji kesalahan logis dalam program Anda. Penyusun hanya dapat memberi tahu Anda tentang kesalahan sintaksis. Jadi, Anda harus menggunakan pernyataan tegas untuk menguji kesalahan logis. Seperti mengatakan menguji sebuah program yang menjual mobil yang hanya BMW yang berwarna biru yang akan mendapatkan diskon 15%. Pengompiler tidak dapat memberi tahu Anda tentang apakah program Anda secara logis benar dalam melakukan ini tetapi pernyataan yang tegas bisa.
sumber
Saya sudah membaca jawaban di sini dan saya pikir saya harus menambahkan perbedaan penting. Ada dua cara yang sangat berbeda di mana penegasan digunakan. Salah satunya adalah sebagai jalan pintas pengembang sementara untuk "Ini seharusnya tidak benar-benar terjadi jadi jika itu membuat saya tahu sehingga saya dapat memutuskan apa yang harus dilakukan", semacam breakpoint bersyarat, untuk kasus-kasus di mana program Anda dapat melanjutkan. Yang lain, adalah sebagai cara untuk menempatkan asumsi tentang status program yang valid dalam kode Anda.
Dalam kasus pertama, pernyataan itu bahkan tidak perlu berada dalam kode akhir. Anda harus menggunakan
Debug.Assert
selama pengembangan dan Anda dapat menghapusnya jika / ketika tidak lagi diperlukan. Jika Anda ingin meninggalkan mereka atau jika Anda lupa untuk menghapusnya tidak ada masalah, karena mereka tidak akan memiliki konsekuensi dalam kompilasi Release.Tetapi dalam kasus kedua, pernyataan adalah bagian dari kode. Mereka, yah, menegaskan, bahwa asumsi Anda benar, dan juga mendokumentasikannya. Dalam hal ini, Anda benar-benar ingin meninggalkannya dalam kode. Jika program dalam kondisi tidak valid, program tidak boleh dilanjutkan. Jika Anda tidak mampu mencapai performa yang baik, Anda tidak akan menggunakan C #. Di satu sisi mungkin berguna untuk melampirkan debugger jika itu terjadi. Di sisi lain, Anda tidak ingin jejak tumpukan muncul pada pengguna Anda dan mungkin lebih penting Anda tidak ingin mereka dapat mengabaikannya. Selain itu, jika dalam layanan itu akan selalu diabaikan. Karenanya dalam produksi, perilaku yang benar adalah dengan melemparkan Pengecualian, dan menggunakan penanganan pengecualian normal pada program Anda, yang mungkin memperlihatkan pesan yang bagus kepada pengguna dan mencatat detailnya.
Trace.Assert
memiliki cara sempurna untuk mencapai ini. Itu tidak akan dihapus dalam produksi, dan dapat dikonfigurasi dengan pendengar yang berbeda menggunakan app.config. Jadi untuk pengembangan, pawang default baik-baik saja, dan untuk produksi Anda dapat membuat TraceListener sederhana seperti di bawah ini yang melempar pengecualian dan mengaktifkannya dalam file konfigurasi produksi.Dan dalam file konfigurasi produksi:
sumber
Saya tidak tahu bagaimana ini dalam C # dan. NET, tetapi dalam C akan menegaskan () hanya bekerja jika dikompilasi dengan -DDEBUG - enduser tidak akan pernah melihat menegaskan () jika itu dikompilasi tanpa. Ini hanya untuk pengembang. Saya sering menggunakannya, kadang-kadang lebih mudah untuk melacak bug.
sumber
Saya tidak akan menggunakannya dalam kode produksi. Lempar pengecualian, tangkap dan catat.
Juga perlu berhati-hati di asp.net, karena pernyataan dapat muncul di konsol dan membekukan permintaan.
sumber