Saya biasanya setuju dengan sebagian besar peringatan analisis kode, dan saya berusaha untuk mematuhinya. Namun, saya mengalami kesulitan dengan yang ini:
CA1031: Jangan menangkap jenis pengecualian umum
Saya mengerti alasan untuk aturan ini. Tetapi, dalam praktiknya, jika saya ingin mengambil tindakan yang sama terlepas dari pengecualian yang dilontarkan, mengapa saya harus menangani masing-masing secara khusus? Selanjutnya, jika saya menangani pengecualian tertentu, bagaimana jika kode yang saya panggil perubahan untuk melemparkan pengecualian baru di masa depan? Sekarang saya harus mengubah kode saya untuk menangani pengecualian baru itu. Sedangkan jika saya hanya menangkap Exception
kode saya tidak perlu berubah.
Misalnya, jika Foo memanggil Bar, dan Foo perlu berhenti memproses terlepas dari jenis pengecualian yang dilemparkan oleh Bar, apakah ada keuntungan untuk menjadi spesifik tentang jenis pengecualian yang saya tangkap?
Mungkin contoh yang lebih baik:
public void Foo()
{
// Some logic here.
LogUtility.Log("some message");
}
public static void Log()
{
try
{
// Actual logging here.
}
catch (Exception ex)
{
// Eat it. Logging failures shouldn't stop us from processing.
}
}
Jika Anda tidak menangkap pengecualian umum di sini, maka Anda harus menangkap setiap jenis pengecualian yang mungkin. Patrick punya poin bagus yang OutOfMemoryException
tidak boleh ditangani dengan cara ini. Jadi bagaimana jika saya ingin mengabaikan setiap pengecualian tetapi OutOfMemoryException
?
sumber
OutOfMemoryException
? Kode penanganan yang sama dengan yang lainnya?OutOfMemoryError
, yang terpisah dariException
pohon warisan karena alasan ituJawaban:
Aturan-aturan ini umumnya merupakan ide yang bagus dan karenanya harus diikuti.
Tapi ingat ini adalah aturan umum. Mereka tidak mencakup semua situasi. Mereka menutupi situasi yang paling umum. Jika Anda memiliki situasi tertentu dan Anda dapat membuat argumen bahwa teknik Anda lebih baik (dan Anda harus dapat menulis komentar dalam kode untuk mengartikulasikan argumen Anda untuk melakukannya) kemudian lakukan (dan kemudian kaji sejawat).
Di sisi berlawanan dari argumen.
Saya tidak melihat contoh Anda di atas sebagai situasi yang baik untuk melakukannya. Jika sistem logging gagal (mungkin mencatat beberapa pengecualian lain) maka saya mungkin tidak ingin aplikasi untuk melanjutkan. Keluar dan cetak pengecualian ke output sehingga pengguna dapat melihat apa yang terjadi.
sumber
Ya, menangkap pengecualian umum adalah hal yang buruk. Pengecualian biasanya berarti bahwa program tidak dapat melakukan apa yang Anda minta.
Ada beberapa jenis pengecualian yang dapat Anda tangani:
Oh, dan sebagai aturan umum: jika Anda tidak tahu apa yang harus dilakukan dengan pengecualian jika Anda menangkapnya, lebih baik gagal cepat (berikan pengecualian ke penelepon dan biarkan itu menanganinya)
sumber
Lingkaran luar paling atas harus memiliki salah satu dari ini untuk mencetak semua yang bisa, dan kemudian mati kematian yang mengerikan, kekerasan dan BISING (karena ini seharusnya tidak terjadi dan seseorang perlu mendengar).
Kalau tidak, Anda biasanya harus sangat berhati-hati karena kemungkinan besar Anda belum mengantisipasi segala sesuatu yang dapat terjadi di lokasi ini, dan karenanya kemungkinan besar tidak akan memperlakukannya dengan benar. Buat sespesifik mungkin sehingga Anda hanya menangkap orang-orang yang Anda kenal akan terjadi, dan biarkan mereka yang tidak terlihat sebelumnya menggelembung hingga kematian yang disebutkan di atas.
sumber
Bukannya itu buruk, hanya saja tangkapan spesifik lebih baik. Ketika Anda spesifik, itu berarti Anda benar-benar memahami, lebih konkret, apa yang sedang dilakukan aplikasi Anda, dan memiliki kontrol lebih besar terhadapnya. Secara umum, jika Anda menemukan situasi di mana Anda hanya menangkap Pengecualian, catat dan lanjutkan, mungkin ada beberapa hal buruk yang terjadi. Jika Anda secara khusus menangkap pengecualian yang Anda tahu bisa dilewati oleh blok kode atau metode, maka ada kemungkinan lebih tinggi Anda benar-benar dapat memulihkan daripada hanya mencatat dan berharap yang terbaik.
sumber
Dua kemungkinan itu tidak saling eksklusif.
Dalam situasi yang ideal, Anda akan menangkap semua jenis pengecualian yang dapat dihasilkan oleh metode Anda, menanganinya berdasarkan per pengecualian, dan pada akhirnya menambahkan
catch
klausa umum untuk menangkap pengecualian di masa depan atau tidak dikenal. Dengan cara ini Anda mendapatkan yang terbaik dari kedua dunia.Ingatlah bahwa untuk menangkap pengecualian yang lebih spesifik, Anda harus mengutamakan mereka.
Sunting: Untuk alasan yang disebutkan dalam komentar, tambahkan rethrow di tangkapan terakhir.
sumber
Saya telah merenungkan hal yang sama baru-baru ini, dan kesimpulan sementara saya adalah bahwa hanya pertanyaan yang muncul karena hirarki .NET Exception sangat kacau.
Ambil contoh, kandidat rendahan
ArgumentNullException
yang mungkin merupakan kandidat yang masuk akal untuk pengecualian yang tidak ingin Anda tangkap, karena cenderung mengindikasikan bug dalam kode daripada kesalahan runtime yang sah. Ah iya. Begitu jugaNullReferenceException
, kecuali ituNullReferenceException
tidak berasal dariSystemException
langsung, jadi tidak ada ember Anda dapat menempatkan semua "kesalahan logis" ke dalam menangkap (atau tidak menangkap).Lalu ada kegagalan utama IMNSHO untukKetika Anda mendapatkanSEHException
memperoleh (melaluiExternalException
)SystemException
dan dengan demikian menjadikannya "normal"SystemException
SEHException
, Anda ingin menulis dump dan mengakhiri secepat mungkin - dan mulai dengan .NET 4 setidaknya beberapa Pengecualian SEH dianggap Pengecualian Keadaan Korup yang tidak akan ditangkap. Suatu hal yang baik, dan fakta yang membuatCA1031
aturan itu semakin tidak berguna, karena sekarang malas Andacatch (Exception)
tidak akan menangkap tipe terburuk ini.Kemudian, tampaknya hal-hal Kerangka Kerja lain agak tidak konsisten diperoleh baik
Exception
secara langsung atau melaluiSystemException
, membuat upaya pengelompokan klausa menangkap sesuatu seperti sesuatu yang diperdebatkan.Ada karya Mr. Lippert yang
C#
terkenal, yang disebut Vexing Exception , di mana ia menjabarkan beberapa kategorisasi pengecualian: Anda bisa berargumen bahwa Anda hanya ingin menangkap yang "eksogen", kecuali ...C#
bahasa dan desain .NET pengecualian kerangka kerja membuat mustahil untuk "menangkap hanya yang eksogen" dengan cara ringkas. (dan, misalnya, seorangOutOfMemoryException
mungkin sangat baik menjadi kesalahan dipulihkan benar-benar normal untuk API yang memiliki mengalokasikan buffer yang entah bagaimana besar)Intinya bagi saya adalah, bahwa cara
C#
menangkap blok bekerja dan cara hierarki pengecualian Kerangka dirancang, aturan diCA1031
luar sama sekali tidak berguna . Ia berpura - pura membantu dengan akar masalah "jangan menelan pengecualian" tetapi menelan pengecualian tidak ada hubungannya dengan apa yang Anda tangkap, tetapi dengan apa yang kemudian Anda lakukan:Ada setidaknya 4 cara untuk sah menangani tertangkap
Exception
, danCA1031
hanya tampaknya dangkal menangani salah satu dari mereka (yaitu kasus re-throw).Sebagai catatan, ada fitur C # 6 yang disebut Filter Pengecualian yang akan membuat
CA1031
sedikit lebih valid lagi sejak itu Anda akan dapat memfilter dengan benar, benar, memfilter pengecualian yang ingin Anda tangkap, dan ada sedikit alasan untuk menulis tanpa filtercatch (Exception)
.sumber
OutOfMemoryException
adalah bahwa tidak ada kode cara yang baik dapat memastikan bahwa itu "hanya" menunjukkan kegagalan alokasi tertentu yang dipersiapkan untuk gagal. Ada kemungkinan bahwa beberapa pengecualian lain yang lebih serius dilemparkan, danOutOfMemoryException
terjadi selama tumpukan terlepas dari pengecualian lain itu. Java mungkin terlambat ke pesta dengan "coba-dengan-sumber dayanya", tetapi menangani pengecualian selama tumpukan sedikit lebih baik daripada .NET.finally
blok terhadap pengecualian yang secara realistis dapat diharapkan terjadi sedemikian rupa sehingga pengecualian asli dan baru keduanya bisa dicatat. Sayangnya, melakukan hal itu seringkali membutuhkan alokasi memori, yang dapat menyebabkan kegagalannya sendiri.Penanganan pengecualian Pokemon (harus menangkap mereka semua!) Tentu tidak selalu buruk. Ketika Anda mengekspos suatu metode kepada klien, terutama pengguna akhir seringkali lebih baik menangkap apa saja dan segalanya daripada membuat aplikasi Anda rusak dan terbakar.
Meskipun mereka harus dihindari di mana pun Anda bisa. Kecuali jika Anda dapat mengambil tindakan spesifik berdasarkan jenis pengecualian, lebih baik tidak menanganinya dan membiarkan pengecualian muncul daripada menelan pengecualian atau menanganinya secara tidak benar.
Lihatlah jawaban SO ini untuk membaca lebih lanjut.
sumber
LoadDocument()
, pada dasarnya tidak mungkin untuk mengidentifikasi semua hal yang mungkin salah, tetapi 99% pengecualian yang dapat dibuang akan berarti "Tidak mungkin untuk menafsirkan konten file dengan nama yang diberikan sebagai sebuah dokumen; atasi itu. " Jika seseorang mencoba untuk membuka sesuatu yang bukan file dokumen yang valid, itu seharusnya tidak merusak aplikasi dan membunuh dokumen terbuka lainnya. Penanganan kesalahan Pokemon dalam kasus seperti itu jelek, tapi saya tidak tahu alternatif yang bagus.Menangkap pengecualian umum itu buruk karena membuat program Anda dalam keadaan tidak terdefinisi. Anda tidak tahu di mana kesalahan terjadi sehingga Anda tidak tahu apa yang sebenarnya telah dilakukan atau tidak dilakukan oleh program Anda.
Di mana saya akan mengizinkan penangkapan semua adalah saat menutup program. Selama Anda bisa membersihkannya dengan baik. Tidak ada yang menyebalkan seperti program yang Anda tutup yang hanya melempar dialog kesalahan yang tidak melakukan apa-apa selain duduk di sana, tidak pergi dan mencegah komputer Anda untuk menutup.
Dalam lingkungan terdistribusi metode log Anda bisa menjadi bumerang: menangkap pengecualian umum bisa berarti program Anda masih memegang kunci pada file log mencegah pengguna lain membuat log.
sumber
Seperti yang dikatakan orang lain, sangat sulit (jika bukan tidak mungkin) untuk membayangkan beberapa tindakan yang ingin Anda lakukan terlepas dari pengecualian yang dilemparkan. Hanya satu contoh situasi di mana negara program telah rusak dan setiap pengolahan lebih lanjut dapat menyebabkan masalah (ini adalah di belakang alasan
Environment.FailFast
).Untuk kode hobi tidak apa-apa untuk ditangkap
Exception
, tetapi untuk kode kelas profesional memperkenalkan jenis pengecualian baru harus diperlakukan dengan hormat yang sama seperti perubahan dalam metode tanda tangan, yaitu dianggap sebagai perubahan melanggar. Jika Anda berlangganan sudut pandang ini maka segera jelas bahwa kembali untuk mengubah (atau memverifikasi) kode klien adalah satu-satunya tindakan yang benar.Tentu, karena Anda tidak akan menangkap hanya pengecualian yang dilemparkan
Bar
. Juga akan ada pengecualian yang dilemparkan klien Bar atau bahkan runtime selama waktu yangBar
ada di tumpukan panggilan. Yang ditulis dengan baikBar
harus mendefinisikan jenis pengecualiannya sendiri jika perlu sehingga penelepon dapat secara spesifik menangkap pengecualian yang dipancarkan dengan sendirinya.IMHO ini adalah cara yang salah untuk berpikir tentang penanganan pengecualian. Anda harus beroperasi pada daftar putih (menangkap pengecualian tipe A dan B), bukan pada daftar hitam (menangkap semua pengecualian selain dari X).
sumber
Mungkin . Ada pengecualian untuk setiap aturan, dan tidak ada aturan yang harus diikuti tanpa kritik. Contoh Anda mungkin merupakan salah satu contoh di mana masuk akal untuk menelan semua pengecualian. Misalnya jika Anda ingin menambahkan penelusuran ke sistem produksi kritis, dan Anda ingin memastikan bahwa perubahan yang Anda lakukan tidak mengganggu tugas utama aplikasi.
Namun , Anda harus memikirkan dengan hati-hati tentang kemungkinan alasan kegagalan sebelum Anda memutuskan untuk diam-diam mengabaikan semuanya. Misalnya, bagaimana jika alasan pengecualiannya adalah:
Tidakkah Anda ingin segera diberitahu bahwa masalah ini ada, sehingga Anda dapat memperbaikinya? Menelan pengecualian berarti Anda tidak pernah tahu ada yang salah.
Beberapa masalah (seperti disk penuh) mungkin juga menyebabkan bagian aplikasi lain gagal - tetapi kegagalan ini tidak dicatat sekarang, jadi Anda tidak pernah tahu!
sumber
Saya ingin membahas ini dari sudut pandang logis daripada perspektif teknis,
Nah, seseorang harus menanganinya. Itulah idenya. Penulis kode pustaka akan lebih berhati-hati dalam menambahkan tipe pengecualian baru karena karena kemungkinan akan menghancurkan klien, Anda sebaiknya tidak sering menjumpai hal ini.
Pertanyaan Anda pada dasarnya adalah "Bagaimana jika saya tidak peduli apa yang salah? Apakah saya benar-benar harus melalui kerumitan untuk mencari tahu apa itu?"
Inilah bagian kecantikannya: tidak, tidak.
"Jadi, bisakah aku melihat ke arah lain dan membuat sesuatu yang jahat muncul di karpet secara otomatis dan selesai dengan itu?"
Tidak, itu juga bukan cara kerjanya.
Intinya adalah, koleksi perkecualian yang mungkin selalu lebih besar dari koleksi yang Anda harapkan dan tertarik dengan konteks masalah kecil lokal Anda. Anda menangani orang-orang yang Anda antisipasi dan jika sesuatu yang tidak terduga terjadi, Anda menyerahkannya ke penangan tingkat yang lebih tinggi daripada menelannya. Jika Anda tidak peduli dengan pengecualian yang tidak Anda harapkan, Anda bertaruh seseorang akan menumpuk panggilan dan akan menjadi sabotase untuk membunuh pengecualian sebelum mencapai handlernya.
"Tapi ... tapi ... maka salah satu pengecualian lain yang tidak aku pedulikan bisa menyebabkan tugasku gagal!"
Iya. Tetapi mereka akan selalu lebih penting daripada yang ditangani secara lokal. Seperti alarm kebakaran atau bos memberitahu Anda untuk menghentikan apa yang Anda lakukan dan mengambil tugas yang lebih mendesak yang muncul.
sumber
Anda harus menangkap pengecualian umum di tingkat teratas dari setiap proses, dan menanganinya dengan melaporkan bug sebaik mungkin dan kemudian menghentikan proses.
Anda seharusnya tidak menangkap pengecualian umum dan mencoba melanjutkan eksekusi.
sumber