Pertimbangkan situasi di mana saya memiliki tiga (atau lebih) cara untuk melakukan penghitungan, yang masing-masing dapat gagal dengan pengecualian. Untuk mencoba setiap perhitungan hingga kami menemukan yang berhasil, saya telah melakukan hal berikut:
double val;
try { val = calc1(); }
catch (Calc1Exception e1)
{
try { val = calc2(); }
catch (Calc2Exception e2)
{
try { val = calc3(); }
catch (Calc3Exception e3)
{
throw new NoCalcsWorkedException();
}
}
}
Apakah ada pola yang diterima yang mencapai ini dengan cara yang lebih baik? Tentu saja saya bisa membungkus setiap kalkulasi dalam metode helper yang mengembalikan nol pada kegagalan, dan kemudian hanya menggunakan ??
operator, tetapi adakah cara untuk melakukan ini secara lebih umum (yaitu tanpa harus menulis metode pembantu untuk setiap metode yang ingin saya gunakan )? Saya telah berpikir tentang menulis metode statis menggunakan obat generik yang membungkus metode tertentu dalam percobaan / penangkapan dan mengembalikan nol pada kegagalan, tetapi saya tidak yakin bagaimana saya akan melakukannya. Ada ide?
sumber
String
keDate
penggunaanSimpleDateFormat.parse
dan saya perlu mencoba beberapa format yang berbeda secara berurutan, pindah ke berikutnya ketika seseorang melempar pengecualian.Jawaban:
Sejauh mungkin, jangan gunakan pengecualian untuk aliran kontrol atau keadaan yang tidak wajar.
Tetapi untuk menjawab pertanyaan Anda secara langsung (dengan asumsi semua tipe pengecualian adalah sama):
sumber
Calc1Exception
,,Calc2Exception
danCalc3Exception
berbagi kelas dasar yang sama.continue
di blok tangkap danbreak
setelah blok tangkap sehingga loop berakhir ketika perhitungan berhasil (terima kasih untuk Lirik untuk bit ini)break
Pernyataan berikutcalc();
dalamtry
(dan tidak adacontinue
pernyataan sama sekali) mungkin sedikit lebih idiomatis.Hanya untuk menawarkan alternatif "di luar kotak", bagaimana dengan fungsi rekursif ...
CATATAN: Saya sama sekali tidak mengatakan bahwa ini adalah cara terbaik untuk menyelesaikan pekerjaan, hanya dengan cara yang berbeda
sumber
return DoCalc(c++)
setara denganreturn DoCalc(c)
- nilai kenaikan pasca tidak akan diteruskan lebih dalam. Untuk membuatnya bekerja (dan memperkenalkan lebih banyak ketidakjelasan) bisa jadi lebih sepertireturn DoCalc((c++,c))
.Anda dapat meratakan sarang dengan menerapkan metode seperti ini:
Tapi saya menduga masalah desain sebenarnya adalah adanya tiga metode berbeda yang pada dasarnya melakukan hal yang sama (dari perspektif pemanggil) tetapi memberikan pengecualian yang berbeda dan tidak terkait.
Ini mengasumsikan tiga pengecualian tidak terkait. Jika mereka semua memiliki kelas dasar yang sama, akan lebih baik menggunakan loop dengan satu blok tangkapan, seperti yang disarankan Ani.
sumber
Cobalah untuk tidak mengontrol logika berdasarkan pengecualian; perhatikan juga bahwa pengecualian harus diberikan hanya dalam kasus luar biasa. Perhitungan dalam banyak kasus tidak boleh menampilkan pengecualian kecuali mereka mengakses sumber daya eksternal atau mengurai string atau sesuatu. Dalam kasus terburuk, ikuti gaya TryMethod (seperti TryParse ()) untuk merangkum logika pengecualian dan membuat aliran kontrol Anda dapat dipelihara dan bersih:
Variasi lain yang memanfaatkan pola Try dan menggabungkan daftar metode alih-alih bersarang jika:
dan jika foreachnya sedikit rumit, Anda dapat membuatnya jelas:
sumber
Buat daftar delegasi ke fungsi kalkulasi Anda dan kemudian miliki waktu berputar untuk melewatinya:
Itu akan memberi Anda gambaran umum tentang bagaimana melakukannya (yaitu, ini bukan solusi yang tepat).
sumber
Exception
saat itu. ;)Exception
.Ini terlihat seperti pekerjaan untuk ... MONADS! Secara khusus, monad Maybe. Mulailah dengan monad Maybe seperti yang dijelaskan di sini . Kemudian tambahkan beberapa metode ekstensi. Saya menulis metode ekstensi ini khusus untuk masalah seperti yang Anda gambarkan. Hal yang menyenangkan tentang monad adalah Anda dapat menulis metode ekstensi yang tepat yang diperlukan untuk situasi Anda.
Gunakan seperti ini:
Jika Anda sering melakukan kalkulasi semacam ini, mungkin monad harus mengurangi jumlah kode boilerplate yang harus Anda tulis sambil meningkatkan keterbacaan kode Anda.
sumber
Maybe
tidak menjadikan ini solusi monadik; itu menggunakan nol dari sifat monadikMaybe
dan mungkin juga hanya menggunakannull
. Selain itu, digunakan "secara monadik" ini akan menjadi kebalikan dariMaybe
. Solusi monad nyata harus menggunakan monad Status yang mempertahankan nilai non-pengecualian pertama sebagai statusnya, tetapi itu akan berlebihan ketika evaluasi berantai normal berfungsi.Versi lain dari pendekatan metode coba . Yang ini memungkinkan pengecualian yang diketik, karena ada jenis pengecualian untuk setiap perhitungan:
sumber
&&
antara setiap kondisi sebagai gantinya.Di Perl Anda bisa melakukannya
foo() or bar()
, yang akan dijalankanbar()
jikafoo()
gagal. Dalam C # kita tidak melihat konstruksi "jika gagal, maka" ini, tetapi ada operator yang dapat kita gunakan untuk tujuan ini: operator penggabungan-nol??
, yang berlanjut hanya jika bagian pertama adalah nol.Jika Anda dapat mengubah tanda tangan perhitungan Anda dan jika Anda membungkus pengecualian mereka (seperti yang ditunjukkan di posting sebelumnya) atau menulis ulang
null
sebagai gantinya untuk mengembalikan , rantai kode Anda menjadi semakin singkat dan masih mudah dibaca:Saya menggunakan penggantian berikut untuk fungsi Anda, yang menghasilkan nilai
40.40
dival
.Saya menyadari bahwa solusi ini tidak selalu dapat diterapkan, tetapi Anda mengajukan pertanyaan yang sangat menarik dan saya yakin, meskipun utasnya relatif lama, bahwa ini adalah pola yang patut dipertimbangkan ketika Anda dapat memperbaikinya.
sumber
Mengingat bahwa metode kalkulasi memiliki tanda tangan tanpa parameter yang sama, Anda dapat mendaftarkannya dalam daftar, dan melakukan iterasi melalui daftar tersebut dan menjalankan metode. Mungkin akan lebih baik bagi Anda untuk menggunakan
Func<double>
arti "fungsi yang mengembalikan hasil tipedouble
".sumber
Anda dapat menggunakan Task / ContinueWith, dan periksa pengecualiannya. Berikut metode ekstensi yang bagus untuk membantu membuatnya cantik:
sumber
Jika tipe aktual dari pengecualian yang dilempar tidak menjadi masalah, Anda bisa menggunakan blok catch tanpa tipe:
sumber
if(succeeded) { break; }
pasca-tangkapan.Dan gunakan seperti itu:
sumber
Sepertinya niat OP adalah mencari pola yang baik untuk menyelesaikan masalahnya dan menyelesaikan masalah yang saat ini sedang ia geluti.
Saya melihat banyak pola bagus yang menghindari blok tangkap percobaan bersarang , yang diposting di umpan ini, tetapi tidak menemukan solusi untuk masalah yang dikutip di atas. Jadi, inilah solusinya:
Seperti yang disebutkan OP di atas, dia ingin membuat objek pembungkus yang mengembalikan
null
kegagalan . Saya akan menyebutnya pod ( Pod yang aman untuk pengecualian ).Tetapi bagaimana jika Anda ingin membuat pod yang aman untuk hasil Jenis Referensi yang dikembalikan oleh fungsi / metode CalcN ().
Jadi, Anda mungkin memperhatikan bahwa tidak perlu "menulis metode pembantu untuk setiap metode yang ingin Anda gunakan" .
The dua jenis polong (untuk
ValueTypeResult
s danReferenceTypeResult
s) yang cukup .Ini kodenya
SafePod
. Ini bukan wadah. Sebaliknya, ini membuat pembungkus delegasi yang aman untuk pengecualian untukValueTypeResult
s danReferenceTypeResult
s.Begitulah cara Anda dapat memanfaatkan operator penggabungan nol yang
??
dikombinasikan dengan kekuatan entitas warga negara kelas satudelegate
.sumber
Anda benar tentang membungkus setiap kalkulasi tetapi Anda harus membungkusnya sesuai dengan prinsip kirim-jangan-tanya.
Pengoperasiannya sederhana, dan dapat mengubah perilakunya secara mandiri. Dan tidak masalah mengapa mereka default. Sebagai buktinya, Anda dapat mengimplementasikan calc1DefaultingToCalc2 sebagai:
sumber
Sepertinya kalkulasi Anda memiliki informasi yang lebih valid untuk dikembalikan daripada hanya kalkulasi itu sendiri. Mungkin akan lebih masuk akal bagi mereka untuk melakukan penanganan pengecualian mereka sendiri dan mengembalikan kelas "hasil" yang berisi informasi kesalahan, informasi nilai, dll. Pikirkan seperti kelas AsyncResult mengikuti pola async. Anda kemudian dapat mengevaluasi hasil penghitungan yang sebenarnya. Anda dapat merasionalisasi ini dengan berpikir bahwa jika penghitungan gagal, itu sama informasinya seperti jika berhasil. Oleh karena itu, pengecualian adalah sepotong informasi, bukan "kesalahan".
Tentu saja, saya ingin tahu lebih banyak tentang keseluruhan arsitektur yang Anda gunakan untuk menyelesaikan kalkulasi ini.
sumber
while (!calcResult.HasValue) nextCalcResult()
, daripada daftar Calc1, Calc2, Calc3 dll.Bagaimana dengan melacak tindakan yang Anda lakukan ...
sumber
track
dalam kasus ini), dan saya tahu persis segmen mana dalam kode saya yang menyebabkan blok gagal. Mungkin Anda harus menjelaskan lebih lanjut untuk memberi tahu anggota seperti DeCaf bahwatrack
pesan tersebut dikirim ke rutinitas penanganan kesalahan kustom Anda yang memungkinkan Anda men-debug kode Anda. Kedengarannya dia tidak mengerti logika Anda.