Kapan saya harus menggunakan Debug.Assert ()?

220

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();
Nicholas Mancuso
sumber
2
Dikotomi yang Anda buat adalah petunjuk. Ini bukan masalah baik-atau untuk pengecualian dan menegaskan, baik-dan untuk kode defensif. Kapan melakukan yang ingin Anda pahami.
Casper Leon Nielsen
5
Saya pernah membaca seseorang menyarankan bahwa pengecualian atau metode menabrak lainnya sesuai untuk kondisi di mana "Tidak mungkin saya dapat pulih dengan bijaksana dari ini", dan juga pernyataan yang sesuai untuk kondisi di mana "Ini seharusnya tidak pernah terjadi, selamanya." Tetapi keadaan realistis apa yang memuaskan kondisi terakhir tanpa memuaskan yang sebelumnya? Berasal dari latar belakang Python di mana menegaskan tetap dalam produksi, saya tidak pernah mengerti pendekatan Java / C # mematikan beberapa validasi Anda dalam produksi. Satu-satunya kasus untuk itu saya benar-benar dapat melihat adalah apakah validasinya mahal.
Mark Amery
2
Secara pribadi saya menggunakan pengecualian untuk metode publik dan pernyataan untuk metode pribadi.
Fred

Jawaban:

230

Dalam Aplikasi Debugging Microsoft .NET 2.0 John Robbins memiliki bagian besar tentang pernyataan. Poin utamanya adalah:

  1. Tegaskan dengan bebas. Anda tidak akan pernah memiliki terlalu banyak pernyataan.
  2. Penegasan tidak menggantikan pengecualian. Pengecualian mencakup hal-hal yang dituntut oleh kode Anda; pernyataan mencakup hal-hal yang diasumsikannya.
  3. Penegasan yang ditulis dengan baik dapat memberi tahu Anda tidak hanya apa yang terjadi dan di mana (seperti pengecualian), tetapi mengapa.
  4. Pesan pengecualian sering kali bersifat samar, mengharuskan Anda untuk bekerja mundur melalui kode untuk menciptakan kembali konteks yang menyebabkan kesalahan. Penegasan dapat mempertahankan status program pada saat kesalahan terjadi.
  5. Pernyataan berfungsi ganda sebagai dokumentasi, memberi tahu pengembang lain tentang asumsi tersirat dari kode Anda.
  6. Dialog yang muncul ketika pernyataan gagal memungkinkan Anda melampirkan debugger ke proses, sehingga Anda bisa melihat-lihat tumpukan seolah-olah Anda telah meletakkan breakpoint di sana.

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.

Rory MacLeod
sumber
3
+1 untuk ringkasan ringkas dan bermanfaat. Sangat langsung berlaku. Namun, hal utama yang hilang bagi saya adalah kapan menggunakan Trace.Assert vs. Trace.Assert. Yaitu sesuatu tentang ketika Anda / tidak ingin mereka dalam kode produksi Anda.
Jon Coombs
2
JonCoombs adalah "Trace.Assert vs Trace.Assert" salah ketik?
themem
1
@thelem Mungkin Jon berarti Debug.Assertvs Trace.Assert. Yang terakhir dieksekusi dalam rilis Rilis serta membangun Debug.
DavidRR
Mengapa saya lebih suka Debug. Meminta pengecualian melempar?
Barış Akkurt
86

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 ada DEBUGkompiler konstan), panggilan untuk Debug.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.

Mark Cidade
sumber
35
Bisakah Anda mengklarifikasi mengapa harus menegaskan jika Anda masih melempar pengecualian sebelum menyebutnya? Atau apakah saya salah mengerti jawaban Anda?
Roman Starkov
2
@romkyns Anda masih harus memasukkannya karena jika tidak, saat Anda membangun proyek dalam mode Rilis , semua validasi / pengecekan kesalahan akan hilang.
Oscar Mederos
28
@Oscar Saya pikir itu inti dari menggunakan pernyataan di tempat pertama ... OK kalau begitu, jadi Anda menempatkan pengecualian sebelum mereka - lalu mengapa menempatkan pernyataan setelah?
Roman Starkov
4
@superjos: Saya harus tidak setuju: Poin # 2 dalam jawaban MacLeod menyatakan bahwa Anda memang membutuhkan pernyataan DAN pengecualian, tetapi tidak di tempat yang sama. Tidak berguna untuk melempar NullRefEx pada variabel dan setelah melakukan Assert di atasnya (metode assert tidak akan pernah menampilkan kotak dialog dalam kasus ini, yang merupakan inti dari pernyataan). Apa yang dimaksud MacLeod adalah bahwa di suatu tempat Anda akan memerlukan pengecualian, di tempat lain sebuah Pernyataan akan cukup.
David
1
Mungkin menjadi berantakan untuk menafsirkan interpretasi saya tentang jawaban orang lain :) Pokoknya saya dengan Anda tentang ini: Anda membutuhkan keduanya, dan Anda tidak harus menempatkan pengecualian sebelum menegaskan. Saya tidak yakin tentang arti "tidak di tempat yang sama". Sekali lagi, menolak untuk menafsirkan, saya hanya akan menyatakan pikiran / preferensi saya: menempatkan satu atau lebih menegaskan untuk memeriksa prasyarat sebelum beberapa operasi dimulai, atau untuk memeriksa postconditions setelah operasi. Selain menegaskan, dan setelah mereka, periksa apakah ada yang salah dan perlu membuang pengecualian.
superjos
52

Dari Kode Selesai

8 Pemrograman Defensif

8.2 Pernyataan

Pernyataan adalah kode yang digunakan selama pengembangan — biasanya suatu rutin atau makro — yang memungkinkan suatu program memeriksa dirinya sendiri saat dijalankan. Ketika pernyataan benar, itu berarti semuanya beroperasi seperti yang diharapkan. Ketika itu salah, itu berarti telah mendeteksi kesalahan tak terduga dalam kode. Misalnya, jika sistem mengasumsikan bahwa file informasi pelanggan tidak akan pernah memiliki lebih dari 50.000 catatan, program mungkin berisi pernyataan bahwa jumlah catatan kurang dari atau sama dengan 50.000. Selama jumlah catatan kurang dari atau sama dengan 50.000, pernyataan itu akan diam. Namun, jika menemukan lebih dari 50.000 catatan, maka akan "menyatakan" dengan keras bahwa ada kesalahan dalam program.

Pernyataan sangat berguna dalam program besar, rumit dan dalam program keandalan tinggi. Mereka memungkinkan pemrogram untuk lebih cepat mengeluarkan asumsi antarmuka yang tidak cocok, kesalahan yang merayap ketika kode dimodifikasi, dan sebagainya.

Penegasan biasanya membutuhkan dua argumen: ekspresi boolean yang menggambarkan asumsi yang seharusnya benar dan pesan untuk ditampilkan jika tidak.

(...)

Biasanya, Anda tidak ingin pengguna melihat pesan pernyataan dalam kode produksi; pernyataan terutama untuk digunakan selama pengembangan dan pemeliharaan. Pernyataan biasanya dikompilasi ke dalam kode pada waktu pengembangan dan dikompilasi keluar dari kode untuk produksi. Selama pengembangan, pernyataan menghilangkan asumsi kontradiktif, kondisi yang tidak terduga, nilai buruk diteruskan ke rutinitas, dan sebagainya. Selama produksi, mereka dikompilasi dari kode sehingga pernyataan tidak menurunkan kinerja sistem.

juan
sumber
7
Jadi, apa yang terjadi jika file informasi pelanggan yang ditemui dalam produksi berisi lebih dari 50.000 catatan? Jika pernyataan dikompilasi dari kode produksi dan situasi ini tidak ditangani, bukankah ini masalah mantra?
DavidRR
1
@ DavidRR Ya, tentu saja. Tetapi segera setelah produksi menandakan masalah dan beberapa pengembang (yang mungkin tidak mengetahui kode ini dengan baik) mendebitkan masalah, pernyataan tersebut akan gagal dan pengembang akan segera tahu bahwa sistem tidak digunakan sebagaimana dimaksud.
Marc
48

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.

Nicholas Piasecki
sumber
3
Ini adalah deskripsi yang sangat jelas tentang praktik yang baik dan memberikan jawaban yang sangat masuk akal untuk pertanyaan yang diajukan.
Casper Leon Nielsen
42

Gunakan konfirmasi untuk memeriksa asumsi dan pengecualian pengembang untuk memeriksa asumsi lingkungan.

Justin R.
sumber
31

Jika aku jadi kamu, aku akan lakukan:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

Atau untuk menghindari pemeriksaan kondisi berulang

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}
Tandai Ingram
sumber
5
Bagaimana ini memecahkan masalah? Dengan ini debug.assert menjadi tidak berguna.
Quibblesome
43
Tidak itu tidak - itu rusak menjadi kode pada saat sebelum pengecualian dilemparkan. Jika Anda memiliki coba / tangkap di tempat lain dalam kode Anda, Anda bahkan mungkin tidak melihat pengecualian!
Tandai Ingram
2
1 Saya telah memiliki banyak masalah di mana orang hanya akan mencoba / menangkap pengecualian tanpa melakukan apa-apa jadi bug pelacakan adalah masalah
dance2die
5
Saya kira ada beberapa kasus di mana Anda mungkin ingin melakukan ini, tetapi Anda seharusnya tidak pernah menangkap pengecualian umum!
Casebash
8
@MarkIngram -1 untuk jawaban Anda, dan +1 pada komentar Anda yang membenarkannya. Ini adalah trik yang bagus untuk keadaan tertentu yang aneh, tetapi sepertinya hal yang aneh dilakukan secara umum untuk semua validasi.
Mark Amery
24

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.

Joe
sumber
1
+1 untuk menunjukkan perbedaan penting antara Debug.Assert dan Trace.Assert, karena OP secara khusus bertanya tentang kode produksi.
Jon Coombs
21

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.

pengguna19113
sumber
11

Pendeknya

Asserts digunakan untuk penjaga dan untuk memeriksa Desain dengan batasan Kontrak, yaitu:

  • Assertsseharusnya 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 Anda
  • Asserts BUKAN suatu mekanisme untuk validasi lini pertama input pengguna atau aturan bisnis
  • Assertsseharusnya 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.
  • Pernyataan yang gagal harus berakibat fatal bagi sistem Anda - yaitu tidak seperti pengecualian, jangan coba-coba menangkap atau menangani gagal 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:

  • Untuk membantu menemukan validasi input pengguna yang hilang, atau bug hulu dalam kode tingkat yang lebih tinggi.
  • Pernyataan dalam basis kode dengan jelas menyampaikan asumsi yang dibuat dalam kode kepada pembaca
  • Penegasan akan diperiksa pada saat runtime dalam Debugbuild.
  • Setelah kode diuji secara mendalam, membangun kembali kode sebagai Release akan menghapus kinerja overhead memverifikasi asumsi (tetapi dengan manfaat bahwa membangun Debug nanti akan selalu mengembalikan cek, jika diperlukan).

... Lebih detail

Debug.Assertmenyatakan 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). - AssertPemeriksaan 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 untuk Assertbahwa 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 menggunakanDebug.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 Dikontrak
  • Contract.Ensures - Kontrak Pasca Kontrak
  • Invariant - 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.
StuartLC
sumber
Sayangnya, Kontrak Kode semuanya mati karena MS telah berhenti mengembangkannya.
Mike Lowery
10

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.

Berdalih
sumber
4
Anda menjawab pantas mendapat beberapa manfaat meskipun saat Anda menyoroti beberapa kekhawatiran yang sering diajukan tentang mereka, fakta bahwa mereka mengganggu sesi debugging dan kesempatan untuk false positive. Namun Anda kehilangan beberapa seluk-beluk dan menulis "optimalkan away asserts" - yang hanya dapat didasarkan pada pemikiran bahwa melemparkan pengecualian dan melakukan debug.assert adalah sama. Bukan, mereka melayani tujuan dan karakteristik yang berbeda, seperti yang dapat Anda lihat dalam beberapa jawaban yang terangkat. Dw
Casper Leon Nielsen
+1 untuk "Yang tidak saya sukai adalah fakta bahwa ia membuat debug build berbeda secara fungsional dengan build rilis. Jika debug menyatakan kegagalan tetapi fungsionalitasnya bekerja dalam rilis, lalu bagaimana itu masuk akal?" Di .NET, System.Diagnostics.Trace.Assert()dijalankan dalam rilis Rilis serta membangun Debug.
DavidRR
7

Menurut Standar IDesign , Anda harus

Tegaskan setiap asumsi. Rata-rata, setiap baris kelima adalah penegasan.

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

Sebagai penafian saya harus menyebutkan bahwa saya belum merasa praktis untuk menerapkan IRL ini. Tapi ini standar mereka.

dewa
sumber
Sepertinya Juval Lowy suka mengutip dirinya sendiri.
devlord
6

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.

Taman Derek
sumber
Di .NET, seseorang dapat menggunakan System.Diagnostics.Trace.Assert()untuk menjalankan pernyataan dalam rilis (produksi) build.
DavidRR
Aturan Analisis Kode CA1062: Memvalidasi argumen metode publik mengharuskan memeriksa argumen nullketika: "Metode yang terlihat secara eksternal mendereferensi salah satu argumen referensi tanpa memverifikasi apakah argumen itu nol ." Dalam situasi seperti itu, metode atau properti harus dibuang ArgumentNullException.
DavidRR
6

Semua pernyataan harus berupa kode yang dapat dioptimalkan untuk:

Debug.Assert(true);

Karena memeriksa sesuatu yang Anda anggap benar. Misalnya:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

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 GetFirstAndConsumedengan nol, sehingga mendapat pengecualian kembali.

Dalam kasus ketiga, ada masalah dengan kode ini, karena seharusnya sudah diperiksa en != nullsebelum dipanggil, sehingga itu tidak benar adalah bug. Atau dengan kata lain, itu harus kode yang secara teoritis dapat dioptimalkan Debug.Assert(true), sicne en != nullharus selalu true!

Jon Hanna
sumber
1
Jadi, dalam kasus ketiga, apa yang terjadi jika en == nulldalam produksi? Apakah Anda mungkin mengatakan bahwa hal itu tidaken == null akan pernah terjadi dalam produksi (karena program ini telah sepenuhnya di-debug)? Jika demikian, maka Debug.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.
DavidRR
@ DavidVRR, saya memang menegaskan bahwa itu tidak pernah bisa nol, dan begitu juga pernyataan dalam kode, maka namanya. Tentu saja saya bisa salah, atau dibuat salah oleh perubahan, dan itulah nilai dari panggilan tegas.
Jon Hanna
1
Panggilan untuk 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.
DavidRR
@ DavidVRR, yang membuatnya tepat hanya ketika saya berpendapat bahwa itu tidak dapat terjadi, karena sekali lagi ini adalah pernyataan fakta, bukan pemeriksaan status. Tentu saja itu juga tidak ada gunanya jika memiliki pernyataan, memiliki bug yang akan ditangkap, namun tidak pernah mengenai kasus itu dalam pengujian.
Jon Hanna
4

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.Findoperasi inti menyatakan itu akan mengembalikan a -1ketika 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.

shannon
sumber
4

Kutipan Diambil dari Programmer Pragmatis: Dari Journeyman hingga Master

Biarkan Pernyataan Diaktifkan

Ada kesalahpahaman umum tentang pernyataan, diumumkan secara resmi oleh orang-orang yang menulis kompiler dan lingkungan bahasa. Bunyinya seperti ini:

Pernyataan menambahkan beberapa overhead ke kode. Karena mereka memeriksa hal-hal yang seharusnya tidak pernah terjadi, mereka hanya akan dipicu oleh bug dalam kode. Setelah kode diuji dan dikirim, mereka tidak lagi diperlukan, dan harus dimatikan untuk membuat kode berjalan lebih cepat. Pernyataan adalah fasilitas debugging.

Ada dua asumsi yang jelas salah di sini. Pertama, mereka berasumsi bahwa pengujian menemukan semua bug. Pada kenyataannya, untuk program kompleks apa pun Anda tidak mungkin menguji bahkan persentase permutasi yang sangat kecil dari kode Anda akan dimasukkan (lihat Pengujian Tanpa Kejujuran).

Kedua, para optimis lupa bahwa program Anda berjalan di dunia yang berbahaya. Selama pengujian, tikus mungkin tidak akan menggerogoti kabel komunikasi, seseorang yang bermain gim tidak akan kehabisan memori, dan file log tidak akan mengisi hard drive. Hal-hal ini mungkin terjadi ketika program Anda berjalan di lingkungan produksi. Garis pertahanan pertama Anda memeriksa kemungkinan kesalahan, dan yang kedua menggunakan pernyataan untuk mencoba mendeteksi kesalahan yang Anda lewatkan.

Menonaktifkan pernyataan ketika Anda mengirimkan suatu program ke produksi adalah seperti menyeberangi kawat tinggi tanpa jaring karena Anda pernah melakukannya secara praktis . Ada nilai dramatis, tetapi sulit mendapatkan asuransi jiwa.

Bahkan jika Anda memang memiliki masalah kinerja, matikan saja pernyataan yang benar-benar menghantam Anda .

Teoman shipahi
sumber
2

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).

Thomas Danecker
sumber
1
Nah, sama dengan beberapa jawaban lain di sini: Anda tidak benar-benar memahami perbedaannya sehingga Anda memilih salah satu dari penawaran, menyiapkan dikotomi palsu di antara mereka dalam proses. Dw
Casper Leon Nielsen
3
Ini adalah satu-satunya jawaban yang benar pada daftar IMO ini. Jangan abaikan begitu saja Casper. Debug Assert adalah anti-pola. Jika invariannya pada waktu debug invariannya pada saat runtime. Mengizinkan aplikasi Anda melanjutkan dengan invarian yang rusak membuat Anda dalam kondisi non-deterministik dan berpotensi masalah yang lebih buruk daripada mogok. IMO lebih baik memiliki kode yang sama di kedua build yang gagal dengan cepat dengan kontrak yang rusak dan kemudian menerapkan penanganan kesalahan yang kuat di tingkat atas. mis. Mengisolasi komponen dan menerapkan kemampuan untuk me-restart mereka (seperti tab yang mogok di browser tidak crash seluruh browser).
justin.m.chase
1
Mungkin bermanfaat untuk menyertakan Trace. Masukkan dalam diskusi Anda di sini, karena tidak dapat diabaikan oleh argumen yang sama.
Jon Coombs
0

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.

orlando calresian
sumber
2
maaf tapi pengecualian melakukan semua hal yang sama, jadi jawaban ini tidak menjawab pertanyaan sebenarnya.
Roman Starkov
0

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.Assertselama 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.Assertmemiliki 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.

using System.Diagnostics;

public class ExceptionTraceListener : DefaultTraceListener
{
    [DebuggerStepThrough]
    public override void Fail(string message, string detailMessage)
    {
        throw new AssertException(message);
    }
}

public class AssertException : Exception
{
    public AssertException(string message) : base(message) { }
}

Dan dalam file konfigurasi produksi:

<system.diagnostics>
  <trace>
    <listeners>
      <remove name="Default"/>
      <add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
    </listeners>
  </trace>
 </system.diagnostics>
AlexDev
sumber
-1

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.

tidak ada
sumber
-1

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.

mattlant
sumber