Saya baru saja memulai pekerjaan pemrograman C #, tapi saya punya sedikit latar belakang di Haskell.
Tapi saya mengerti C # adalah bahasa berorientasi objek, saya tidak ingin memaksa pasak bundar ke dalam lubang persegi.
Saya membaca artikel Melempar Pengecualian dari Microsoft yang menyatakan:
JANGAN mengembalikan kode kesalahan.
Tetapi karena sudah terbiasa dengan Haskell, saya telah menggunakan tipe data C # OneOf
, mengembalikan hasilnya sebagai nilai "benar" atau kesalahan (paling sering Pencacahan) sebagai nilai "kiri".
Ini seperti konvensi Either
di Haskell.
Bagi saya, ini tampaknya lebih aman daripada pengecualian. Di C #, mengabaikan pengecualian tidak menghasilkan kesalahan kompilasi, dan jika tidak ketahuan, mereka hanya menggembung dan merusak program Anda. Ini mungkin lebih baik daripada mengabaikan kode kesalahan dan menghasilkan perilaku yang tidak terdefinisi, tetapi menabrak perangkat lunak klien Anda masih bukan hal yang baik, terutama ketika melakukan banyak tugas bisnis penting lainnya di latar belakang.
Dengan OneOf
, kita harus cukup eksplisit tentang membongkar dan menangani nilai kembali dan kode kesalahan. Dan jika seseorang tidak tahu bagaimana menanganinya pada tahap itu di tumpukan panggilan, itu harus dimasukkan ke dalam nilai balik dari fungsi saat ini, sehingga penelepon tahu kesalahan bisa terjadi.
Tapi ini sepertinya bukan pendekatan yang disarankan Microsoft.
Apakah menggunakan OneOf
bukannya pengecualian untuk menangani pengecualian "biasa" (seperti File Tidak Ditemukan dll) merupakan pendekatan yang masuk akal atau apakah itu praktik yang mengerikan?
Perlu dicatat bahwa saya pernah mendengar bahwa pengecualian sebagai aliran kontrol dianggap sebagai antipattern yang serius , jadi jika "pengecualian" adalah sesuatu yang biasanya Anda tangani tanpa mengakhiri program, bukankah "aliran kendali" itu ada hubungannya? Saya mengerti ada sedikit area abu-abu di sini.
Catatan Saya tidak menggunakan OneOf
untuk hal-hal seperti "Kehabisan Memori", kondisi yang saya tidak harapkan untuk pulih masih akan melempar pengecualian. Tapi saya merasa masalah yang cukup masuk akal, seperti input pengguna yang tidak menguraikan pada dasarnya adalah "aliran kontrol" dan mungkin tidak boleh melempar pengecualian.
Pikiran selanjutnya:
Dari diskusi ini apa yang saya ambil saat ini adalah sebagai berikut:
- Jika Anda mengharapkan penelepon langsung ke
catch
dan menangani pengecualian sebagian besar waktu dan melanjutkan pekerjaannya, mungkin melalui jalur lain, itu mungkin harus menjadi bagian dari tipe pengembalian.Optional
atauOneOf
bisa bermanfaat disini. - Jika Anda mengharapkan penelepon segera tidak menangkap pengecualian sebagian besar waktu, melemparkan pengecualian, untuk menyimpan kekonyolan melewatkannya secara manual ke tumpukan.
- Jika Anda tidak yakin apa yang akan dilakukan penelepon langsung, mungkin berikan keduanya, seperti
Parse
danTryParse
.
sumber
Result
;-)FileNotFoundException
.Either
monad bukan kode kesalahan , dan juga bukanOneOf
? Mereka pada dasarnya berbeda, dan pertanyaannya tampaknya didasarkan pada kesalahpahaman. (Meskipun dalam bentuk yang dimodifikasi itu masih pertanyaan yang valid.)Jawaban:
Ini pastinya adalah hal yang baik.
Anda ingin apa pun yang membuat sistem dalam keadaan tidak terdefinisi untuk menghentikan sistem karena sistem yang tidak terdefinisi dapat melakukan hal-hal buruk seperti data yang korup, memformat hard drive, dan mengirim presiden mengancam email. Jika Anda tidak dapat memulihkan dan mengembalikan sistem ke kondisi yang ditentukan, maka menabrak adalah hal yang harus dilakukan. Itulah tepatnya mengapa kita membangun sistem yang crash daripada diam-diam memisahkan diri. Sekarang tentu saja, kita semua menginginkan sistem stabil yang tidak pernah crash tetapi kami hanya benar-benar menginginkannya ketika sistem tetap dalam kondisi aman yang dapat diprediksi yang telah ditentukan.
Itu benar sekali tetapi sering disalahpahami. Ketika mereka menemukan sistem pengecualian, mereka takut melanggar pemrograman terstruktur. Pemrograman terstruktur adalah mengapa kita memiliki
for
,while
,until
,break
, dancontinue
ketika semua yang kita butuhkan, untuk melakukan semua itu, adalahgoto
.Dijkstra mengajarkan kepada kita bahwa menggunakan goto secara informal (yaitu, melompat-lompat di mana pun Anda suka) membuat membaca kode menjadi mimpi buruk. Ketika mereka memberi kami sistem pengecualian, mereka takut menciptakan kembali kebagian. Jadi mereka mengatakan kepada kami untuk tidak "menggunakannya untuk kontrol aliran" berharap kami akan mengerti. Sayangnya, banyak dari kita tidak.
Anehnya, kita tidak sering menyalahgunakan pengecualian untuk membuat kode spageti seperti yang biasa kita lakukan dengan goto. Saran itu sendiri tampaknya telah menyebabkan lebih banyak masalah.
Pengecualian pada dasarnya adalah tentang menolak asumsi. Ketika Anda meminta agar suatu file disimpan, Anda menganggap bahwa file tersebut dapat dan akan disimpan. Pengecualian yang Anda dapatkan ketika itu tidak mungkin tentang nama yang ilegal, HD yang penuh, atau karena tikus telah menggerogoti kabel data Anda. Anda dapat menangani semua kesalahan itu secara berbeda, Anda dapat menangani semuanya dengan cara yang sama, atau Anda dapat membiarkannya menghentikan sistem. Ada jalur bahagia di kode Anda di mana asumsi Anda harus benar. Salah satu cara atau pengecualian lain membawa Anda keluar dari jalan bahagia itu. Sebenarnya, ya itu semacam "kontrol aliran" tapi bukan itu yang mereka peringatkan. Mereka berbicara tentang omong kosong seperti ini :
"Pengecualian harus luar biasa". Tautologi kecil ini lahir karena perancang sistem pengecualian membutuhkan waktu untuk membangun jejak tumpukan. Dibandingkan dengan melompat-lompat, ini lambat. Itu memakan waktu CPU. Tetapi jika Anda akan log dan menghentikan sistem atau setidaknya menghentikan pemrosesan intensif waktu saat ini sebelum memulai yang berikutnya maka Anda memiliki waktu untuk mematikan. Jika orang mulai menggunakan pengecualian "untuk kontrol aliran" asumsi-asumsi tentang waktu semua keluar jendela. Jadi "Pengecualian harus luar biasa" benar-benar diberikan kepada kami sebagai pertimbangan kinerja.
Jauh lebih penting daripada itu tidak membingungkan kita. Berapa lama waktu yang Anda butuhkan untuk menemukan infinite loop pada kode di atas?
... adalah saran yang bagus ketika Anda berada di basis kode yang biasanya tidak menggunakan kode kesalahan. Mengapa? Karena tidak ada yang akan mengingat untuk menyimpan nilai pengembalian dan memeriksa kode kesalahan Anda. Ini masih merupakan konvensi yang bagus ketika Anda berada di C.
Anda menggunakan konvensi lain lagi. Tidak masalah selama Anda mengatur konvensi dan tidak hanya melawan yang lain. Sangat membingungkan memiliki dua konvensi kesalahan dalam basis kode yang sama. Jika entah bagaimana Anda sudah menyingkirkan semua kode yang menggunakan konvensi lain, silakan.
Saya suka konvensi itu sendiri. Salah satu penjelasan terbaiknya saya temukan di sini * :
Tetapi sebanyak yang saya suka saya masih tidak akan mencampurnya dengan konvensi lain. Pilih satu dan pertahankan. 1
1: Maksud saya jangan membuat saya memikirkan lebih dari satu kebaktian pada saat yang bersamaan.
Ini benar-benar tidak sesederhana ini. Salah satu hal mendasar yang perlu Anda pahami adalah apa itu nol.
Berapa hari tersisa di bulan Mei? 0 (karena ini bukan bulan Mei. Sudah bulan Juni).
Pengecualian adalah cara untuk menolak asumsi tetapi bukan satu-satunya cara. Jika Anda menggunakan pengecualian untuk menolak asumsi Anda meninggalkan jalan bahagia. Tetapi jika Anda memilih nilai untuk mengirim jalan bahagia yang menandakan bahwa hal-hal tidak sesederhana seperti yang diasumsikan maka Anda bisa tetap berada di jalur itu selama itu bisa berurusan dengan nilai-nilai itu. Terkadang 0 sudah digunakan untuk mengartikan sesuatu sehingga Anda harus menemukan nilai lain untuk memetakan asumsi penolakan Anda. Anda mungkin mengenali ide ini dari penggunaannya dalam aljabar lama yang bagus . Monads dapat membantu dengan itu tetapi tidak harus selalu menjadi monad.
Misalnya 2 :
Bisakah Anda memikirkan alasan bagus mengapa ini harus dirancang sedemikian rupa sehingga dengan sengaja melemparkan apa pun? Tebak apa yang Anda dapatkan ketika int tidak bisa diuraikan? Aku bahkan tidak perlu memberitahumu.
Itu pertanda nama baik. Maaf tapi TryParse bukan ide saya tentang nama baik.
Kita sering menghindari melempar perkecualian untuk tidak mendapatkan apa-apa ketika jawabannya bisa lebih dari satu hal pada saat yang sama tetapi untuk beberapa alasan jika jawabannya adalah satu hal atau tidak sama sekali, kita terobsesi untuk bersikeras bahwa itu memberi kita satu hal atau lempar:
Apakah garis paralel benar-benar perlu menyebabkan pengecualian di sini? Benarkah seburuk itu jika daftar ini tidak akan mengandung lebih dari satu poin?
Mungkin secara semantik Anda tidak bisa menerimanya. Jika demikian, sangat disayangkan. Tapi Mungkin Monads, yang tidak memiliki ukuran sewenang-wenang seperti
List
itu, akan membuat Anda merasa lebih baik tentang hal itu.The Monads adalah koleksi tujuan khusus kecil yang dimaksudkan untuk digunakan dengan cara tertentu yang menghindari perlu mengujinya. Kita seharusnya menemukan cara untuk berurusan dengan mereka terlepas dari apa yang dikandungnya. Dengan begitu jalan yang bahagia tetap sederhana. Jika Anda membuka dan menguji setiap Monad yang Anda sentuh, Anda salah menggunakannya.
Saya tahu, ini aneh. Tetapi ini adalah alat baru (bagi kami). Jadi berikan waktu. Palu lebih masuk akal ketika Anda berhenti menggunakannya pada sekrup.
Jika Anda akan memanjakan saya, saya ingin membahas komentar ini:
Ini mutlak benar. Monads jauh lebih dekat dengan koleksi daripada pengecualian, bendera, atau kode kesalahan. Mereka membuat wadah yang bagus untuk hal-hal seperti itu ketika digunakan dengan bijak.
sumber
throw
tidak benar-benar tahu / mengatakan di mana kita akan melompat;)C # bukan Haskell, dan Anda harus mengikuti konsensus ahli dari komunitas C #. Jika Anda mencoba mengikuti praktik Haskell dalam proyek C #, Anda akan mengasingkan orang lain di tim Anda, dan pada akhirnya Anda mungkin akan menemukan alasan mengapa komunitas C # melakukan sesuatu secara berbeda. Satu alasan besar adalah bahwa C # tidak dengan mudah mendukung serikat yang didiskriminasi.
Ini bukan kebenaran yang diterima secara universal. Pilihan dalam setiap bahasa yang mendukung pengecualian adalah melempar pengecualian (yang tidak dapat ditangani oleh penelepon) atau mengembalikan nilai majemuk (yang harus ditangani oleh penelepon).
Menyebarkan kondisi kesalahan ke atas melalui tumpukan panggilan membutuhkan kondisi pada setiap tingkat, menggandakan kompleksitas siklomatik dari metode tersebut dan akibatnya menggandakan jumlah kasus uji unit. Dalam aplikasi bisnis yang umum, banyak pengecualian yang tidak dapat dipulihkan, dan dapat dibiarkan menyebar ke level tertinggi (misalnya, titik masuk layanan aplikasi web).
sumber
Anda datang ke C # pada waktu yang menarik. Sampai relatif baru-baru ini, bahasa tersebut berada di ruang pemrograman imperatif. Menggunakan pengecualian untuk mengkomunikasikan kesalahan jelas merupakan norma. Menggunakan kode kembali menderita karena kurangnya dukungan bahasa (misalnya di sekitar serikat terdiskriminasi dan pencocokan pola). Cukup wajar, panduan resmi dari Microsoft adalah untuk menghindari kode pengembalian dan menggunakan pengecualian.
Selalu ada pengecualian untuk ini, dalam bentuk
TryXXX
metode, yang akan mengembalikanboolean
hasil keberhasilan dan menyediakan hasil nilai kedua melaluiout
parameter. Ini sangat mirip dengan pola "coba" dalam pemrograman fungsional, kecuali bahwa hasilnya datang melalui parameter keluar, bukan melaluiMaybe<T>
nilai balik.Tapi segalanya berubah. Pemrograman fungsional menjadi lebih populer dan bahasa seperti C # merespons. C # 7.0 memperkenalkan beberapa fitur dasar pencocokan pola ke bahasa; C # 8 akan memperkenalkan lebih banyak lagi, termasuk ekspresi switch, pola rekursif dll. Seiring dengan ini, telah ada pertumbuhan "perpustakaan fungsional" untuk C #, seperti perpustakaan Succinc <T> saya sendiri , yang menyediakan dukungan untuk serikat yang didiskriminasi juga .
Gabungan dua hal ini berarti bahwa kode seperti berikut ini perlahan-lahan semakin populer.
Itu masih cukup ceruk saat ini, meskipun bahkan dalam dua tahun terakhir saya telah melihat perubahan yang ditandai dari permusuhan umum antara pengembang C # untuk kode seperti itu menjadi minat yang tumbuh dalam menggunakan teknik tersebut.
Kami belum ada di sana mengatakan " JANGAN mengembalikan kode kesalahan." kuno dan perlu pensiun. Tetapi pawai di jalan menuju tujuan itu sedang berlangsung. Jadi pilihlah: tetap dengan cara-cara lama melemparkan pengecualian dengan meninggalkan gay jika itu cara Anda suka melakukan sesuatu; atau mulai menjelajahi dunia baru di mana konvensi dari bahasa fungsional menjadi lebih populer di C #.
sumber
java.util.Stream
(mirip IEnumerable-setengah) dan lambda sekarang, kan? :)Saya pikir itu sangat masuk akal untuk menggunakan DU untuk mengembalikan kesalahan, tapi kemudian saya akan mengatakan itu ketika saya menulis OneOf :) Tetapi saya tetap akan mengatakan itu, karena ini adalah praktik umum di F # dll (misalnya jenis Hasil, seperti yang disebutkan oleh orang lain ).
Saya tidak memikirkan Kesalahan yang biasanya saya kembalikan sebagai situasi luar biasa, tetapi sebagai normal, tetapi semoga jarang menghadapi situasi yang mungkin, atau mungkin tidak perlu ditangani, tetapi harus dimodelkan.
Inilah contoh dari halaman proyek.
Melontarkan pengecualian untuk kesalahan ini sepertinya berlebihan - mereka memiliki overhead kinerja, selain dari kerugian tidak eksplisit dalam tanda tangan metode, atau menyediakan pencocokan lengkap.
Sejauh mana OneOf idiomatis dalam c #, itu pertanyaan lain. Sintaksnya valid dan cukup intuitif. DU dan pemrograman berorientasi rel adalah konsep yang sudah dikenal luas.
Saya akan menyimpan Pengecualian untuk hal-hal yang mengindikasikan ada sesuatu yang rusak jika memungkinkan.
sumber
OneOf
tentang membuat nilai pengembalian lebih kaya, seperti mengembalikan Nullable. Ini menggantikan pengecualian untuk situasi yang tidak biasa untuk memulai.OneOf
kira-kira setara dengan pengecualian yang diperiksa di Jawa. Ada beberapa perbedaan:OneOf
tidak bekerja dengan metode yang menghasilkan metode yang disebut efek sampingnya (karena dapat disebut secara bermakna dan hasilnya diabaikan). Jelas, kita semua harus mencoba menggunakan fungsi murni, tetapi ini tidak selalu mungkin.OneOf
tidak memberikan informasi tentang di mana masalah terjadi. Ini bagus karena menghemat kinerja (pengecualian yang dilemparkan di Jawa mahal karena mengisi jejak tumpukan, dan dalam C # itu akan sama) dan memaksa Anda untuk memberikan informasi yang cukup di anggota kesalahanOneOf
itu sendiri. Ini juga buruk karena informasi ini mungkin tidak mencukupi dan Anda dapat kesulitan menemukan asal masalahnya.OneOf
dan pengecualian yang diperiksa memiliki satu "fitur" penting yang sama:Ini mencegah rasa takut Anda
tetapi seperti yang sudah dikatakan, ketakutan ini tidak rasional. Pada dasarnya, biasanya ada satu tempat di program Anda, di mana Anda perlu menangkap semuanya (kemungkinan Anda sudah akan menggunakan kerangka kerja melakukannya). Karena Anda dan tim Anda biasanya menghabiskan waktu berminggu-minggu atau bertahun-tahun untuk mengerjakan program ini, Anda tidak akan melupakannya, bukan?
Keuntungan besar dari pengecualian yang dapat diabaikan adalah bahwa mereka dapat ditangani di mana pun Anda ingin menanganinya, daripada di mana-mana di tumpukan jejak. Ini menghilangkan banyak boilerplate (lihat saja beberapa kode Java yang menyatakan "melempar ...." atau membungkus pengecualian) dan itu juga membuat kode Anda kurang buggy: Karena metode apa pun mungkin melempar, Anda harus menyadarinya. Untungnya, tindakan yang benar biasanya tidak melakukan apa-apa , yaitu membiarkannya menggelembung ke tempat yang bisa ditangani secara wajar.
sumber
OneOf<SomeSuccess, SomeErrorInfo>
manaSomeErrorInfo
memberikan rincian kesalahan.Kesalahan memiliki sejumlah dimensi. Saya mengidentifikasi tiga dimensi: dapatkah mereka dicegah, seberapa sering mereka terjadi, dan apakah mereka dapat dipulihkan. Sementara itu, pensinyalan kesalahan terutama memiliki satu dimensi: memutuskan sejauh mana mengalahkan pemanggil di atas kepala untuk memaksa mereka menangani kesalahan, atau membiarkan kesalahan "diam-diam" meluap sebagai pengecualian.
Kesalahan yang tidak dapat dicegah, sering, dan dapat dipulihkan adalah kesalahan yang benar-benar perlu ditangani di situs panggilan. Mereka membenarkan memaksa penelepon untuk menghadapi mereka. Di Jawa, saya membuat mereka diperiksa pengecualian, dan efek yang sama dicapai dengan membuat mereka
Try*
metode atau kembali serikat diskriminasi. Serikat yang didiskriminasi sangat baik ketika suatu fungsi memiliki nilai balik. Mereka adalah versi pengembalian yang jauh lebih halusnull
. Sintakstry/catch
blok tidak terlalu bagus (terlalu banyak tanda kurung), membuat alternatif terlihat lebih baik. Juga, pengecualian sedikit lambat karena mereka merekam jejak tumpukan.Kesalahan yang dapat dicegah (yang tidak dicegah karena kesalahan / kelalaian programmer) dan kesalahan yang tidak dapat dipulihkan berfungsi dengan sangat baik sebagai pengecualian biasa. Kesalahan yang jarang terjadi juga berfungsi lebih baik sebagai pengecualian biasa karena programmer sering kali menimbangnya tidak sepadan dengan upaya untuk mengantisipasi (itu tergantung pada tujuan program).
Poin penting adalah seringnya situs yang menentukan bagaimana mode kesalahan suatu metode cocok dengan dimensi ini, yang harus diingat ketika mempertimbangkan hal-hal berikut.
Dalam pengalaman saya, memaksa penelepon untuk menghadapi kondisi kesalahan baik-baik saja ketika kesalahan tidak dapat dicegah, sering, dan dapat dipulihkan, tetapi menjadi sangat menjengkelkan ketika penelepon dipaksa untuk melompat melalui lingkaran ketika mereka tidak perlu atau ingin . Ini mungkin karena penelepon tahu kesalahan tidak akan terjadi, atau karena mereka (belum) ingin membuat kode tangguh. Di Jawa, ini menjelaskan kekhawatiran terhadap seringnya menggunakan pengecualian yang diperiksa dan mengembalikan Opsional (yang mirip dengan Mungkin dan baru-baru ini diperkenalkan di tengah banyak kontroversi). Jika ragu, lempar pengecualian dan biarkan penelepon memutuskan bagaimana mereka ingin menanganinya.
Terakhir, perlu diingat bahwa hal yang paling penting tentang kondisi kesalahan, jauh di atas pertimbangan lain seperti bagaimana mereka diberi sinyal, adalah untuk mendokumentasikannya secara menyeluruh .
sumber
Jawaban lain telah membahas pengecualian vs kode kesalahan secara cukup rinci, jadi saya ingin menambahkan perspektif lain yang spesifik untuk pertanyaan:
OneOf bukan kode kesalahan, ini lebih seperti sebuah monad
Cara itu digunakan,
OneOf<Value, Error1, Error2>
itu adalah wadah yang mewakili hasil aktual atau beberapa kesalahan. Itu seperti sebuahOptional
, kecuali bahwa ketika tidak ada nilai yang hadir, itu dapat memberikan rincian lebih lanjut mengapa itu ada.Masalah utama dengan kode kesalahan adalah Anda lupa memeriksanya. Tapi di sini, Anda benar-benar tidak dapat mengakses hasilnya tanpa memeriksa kesalahan.
Satu-satunya masalah adalah ketika Anda tidak peduli dengan hasilnya. Contoh dari dokumentasi OneOf memberikan:
... dan kemudian mereka membuat respons HTTP berbeda untuk setiap hasil, yang jauh lebih baik daripada menggunakan pengecualian. Tetapi jika Anda memanggil metode seperti ini.
maka Anda tidak memiliki masalah dan pengecualian akan menjadi pilihan yang lebih baik. Bandingkan Rail
save
vssave!
.sumber
Satu hal yang perlu Anda pertimbangkan adalah bahwa kode dalam C # umumnya tidak murni. Dalam basis kode yang penuh dengan efek samping, dan dengan kompiler yang tidak peduli dengan apa yang Anda lakukan dengan nilai pengembalian beberapa fungsi, pendekatan Anda dapat menyebabkan Anda sangat sedih. Misalnya, jika Anda memiliki metode yang menghapus file, Anda ingin memastikan pemberitahuan aplikasi ketika metode gagal, meskipun tidak ada alasan untuk memeriksa kode kesalahan "di jalan bahagia". Ini adalah perbedaan besar dari bahasa fungsional yang mengharuskan Anda untuk secara eksplisit mengabaikan nilai pengembalian yang tidak Anda gunakan. Dalam C #, nilai kembali selalu diabaikan secara implisit (satu-satunya pengecualian adalah properti getter IIRC).
Alasan mengapa MSDN memberitahu Anda "untuk tidak mengembalikan kode kesalahan" adalah persis ini - tidak ada yang memaksa Anda untuk bahkan membaca kode kesalahan. Kompiler sama sekali tidak membantu Anda. Namun, jika fungsi Anda bebas efek samping, Anda dapat dengan aman menggunakan sesuatu seperti
Either
- intinya adalah bahwa bahkan jika Anda mengabaikan hasil kesalahan (jika ada), Anda hanya dapat melakukannya jika Anda tidak menggunakan "hasil yang tepat" antara. Ada beberapa cara bagaimana Anda bisa mencapai ini - misalnya, Anda mungkin hanya mengizinkan "membaca" nilai dengan melewati delegasi untuk menangani kasus-kasus keberhasilan dan kesalahan, dan Anda dapat dengan mudah menggabungkannya dengan pendekatan seperti Pemrograman Berorientasi Kereta Api (itu tidak bekerja dengan baik di C #).int.TryParse
ada di suatu tempat di tengah. Itu murni. Ini mendefinisikan apa nilai dari dua hasil setiap saat - sehingga Anda tahu bahwa jika nilai kembalifalse
, parameter output akan diatur ke0
. Itu masih tidak mencegah Anda menggunakan parameter output tanpa memeriksa nilai kembali, tetapi setidaknya Anda dijamin apa hasilnya meskipun fungsi gagal.Tetapi satu hal yang sangat penting di sini adalah konsistensi . Kompiler tidak akan menyelamatkan Anda jika seseorang mengubah fungsi itu untuk memiliki efek samping. Atau untuk melempar pengecualian. Jadi, meskipun pendekatan ini baik-baik saja dalam C # (dan saya menggunakannya), Anda perlu memastikan bahwa semua orang di tim Anda memahami dan menggunakannya . Banyak invarian yang diperlukan untuk pemrograman fungsional agar berfungsi dengan baik tidak dipaksakan oleh kompiler C #, dan Anda perlu memastikan bahwa mereka diikuti sendiri. Anda harus tetap sadar akan hal-hal yang melanggar jika Anda tidak mengikuti aturan yang diterapkan Haskell secara default - dan ini adalah sesuatu yang Anda ingat untuk paradigma fungsional apa pun yang Anda perkenalkan ke kode Anda.
sumber
Pernyataan yang benar adalah Jangan menggunakan kode kesalahan untuk pengecualian! Dan jangan gunakan pengecualian untuk non-pengecualian.
Jika sesuatu terjadi sehingga kode Anda tidak dapat dipulihkan (yaitu membuatnya berfungsi), itu merupakan pengecualian. Tidak ada kode langsung Anda akan lakukan dengan kode kesalahan kecuali menyerahkannya ke atas untuk login atau mungkin memberikan kode kepada pengguna dengan cara tertentu. Namun inilah tepatnya pengecualian - mereka berurusan dengan semua penyerahan dan membantu untuk menganalisis apa yang sebenarnya terjadi.
Namun, jika sesuatu terjadi, seperti input tidak valid yang dapat Anda tangani, baik dengan memformat ulang secara otomatis atau dengan meminta pengguna untuk memperbaiki data (dan Anda memikirkan hal itu), itu sebenarnya bukan pengecualian di tempat pertama - karena Anda harapkan itu. Anda dapat merasa bebas untuk menangani kasus-kasus tersebut dengan kode kesalahan atau arsitektur lainnya. Hanya saja tidak ada pengecualian.
(Perhatikan bahwa saya tidak terlalu berpengalaman dalam C #, tetapi jawaban saya harus berlaku dalam kasus umum berdasarkan tingkat konseptual dari apa pengecualian itu.)
sumber
catch
kata kunci tidak akan ada. Dan karena kita berbicara dalam konteks C #, perlu dicatat bahwa filosofi yang dianut di sini tidak diikuti oleh .NET framework; mungkin contoh paling sederhana yang bisa dibayangkan dari "input tidak valid yang dapat Anda tangani" adalah pengguna mengetikkan non-angka dalam bidang di mana angka diharapkan ... danint.Parse
melempar pengecualian.Ini adalah 'Ide Mengerikan'.
Ini mengerikan karena, setidaknya di .net, Anda tidak memiliki daftar lengkap tentang kemungkinan pengecualian. Kode apa pun mungkin dapat membuang pengecualian apa pun. Terutama jika Anda melakukan OOP dan bisa memanggil metode yang diganti pada tipe sub daripada tipe yang dideklarasikan
Jadi Anda harus mengubah semua metode dan fungsi
OneOf<Exception, ReturnType>
, maka jika Anda ingin menangani berbagai jenis pengecualian Anda harus memeriksa jenisnyaIf(exception is FileNotFoundException)
dlltry catch
adalah setara denganOneOf<Exception, ReturnType>
Sunting ----
Saya sadar Anda mengusulkan untuk kembali,
OneOf<ErrorEnum, ReturnType>
tetapi saya merasa ini adalah perbedaan tanpa perbedaan.Anda menggabungkan kode pengembalian, pengecualian yang diharapkan, dan bercabang dengan pengecualian. Tetapi efek keseluruhannya sama.
sumber
Exception
.