Niedawno rozpocząłem programowanie w języku C #, ale mam spore doświadczenie w Haskell.
Ale rozumiem, że C # jest językiem zorientowanym obiektowo, nie chcę wciskać okrągłego kołka w kwadratowy otwór.
Przeczytałem artykuł Microsoft dotyczący wyjątku Zgłaszanie wyjątków :
NIE zwracaj kodów błędów.
Ale przyzwyczajony do Haskell, używałem typu danych C # OneOf
, zwracając wynik jako „właściwą” wartość lub błąd (najczęściej wyliczenie) jako „lewą” wartość.
To bardzo przypomina konwencję Either
w Haskell.
Wydaje mi się to bezpieczniejsze niż wyjątki. W języku C # ignorowanie wyjątków nie powoduje błędu kompilacji, a jeśli nie zostaną złapane, po prostu pękają i powodują awarię programu. Być może jest to lepsze niż ignorowanie kodu błędu i nieokreślone zachowanie, ale zawieszanie oprogramowania klienta nadal nie jest dobrą rzeczą, szczególnie gdy wykonuje wiele innych ważnych zadań biznesowych w tle.
Za pomocą OneOf
należy wyraźnie powiedzieć o rozpakowaniu i obsłudze wartości zwracanej i kodów błędów. A jeśli nie wiadomo, jak sobie z tym poradzić na tym etapie na stosie wywołań, należy wprowadzić wartość zwracaną przez bieżącą funkcję, aby wywołujący wiedzieli, że może to spowodować błąd.
Ale to nie wydaje się podejście sugerowane przez Microsoft.
Czy stosowanie OneOf
zamiast wyjątków obsługi „zwykłych” wyjątków (takich jak nie znaleziono pliku itp.) Jest rozsądnym podejściem, czy też jest to okropna praktyka?
Warto zauważyć, że słyszałem, że wyjątki jako przepływ sterowania są uważane za poważny antypattern , więc jeśli „wyjątek” jest czymś, z czym normalnie byś sobie poradził bez kończenia programu, czyż nie jest to „przepływ sterowania”? Rozumiem, że jest tu trochę szarej strefy.
Uwaga: Nie używam OneOf
do takich rzeczy jak „Brak pamięci”, warunki, od których nie oczekuję powrotu do normy, nadal będą zgłaszać wyjątki. Ale wydaje mi się, że są to dość rozsądne problemy, takie jak dane wejściowe użytkownika, które nie analizują, są w zasadzie „kontrolą przepływu” i prawdopodobnie nie powinny generować wyjątków.
Kolejne przemyślenia:
Z tej dyskusji obecnie zabieram następujące rzeczy:
- Jeśli oczekujesz, że bezpośredni dzwoniący
catch
zajmie się wyjątkiem i będzie go obsługiwał przez większość czasu i będzie kontynuował pracę, być może inną ścieżką, prawdopodobnie powinien być częścią typu zwracanego.Optional
lubOneOf
może być przydatny tutaj. - Jeśli oczekujesz, że bezpośredni dzwoniący nie będzie wychwytywał wyjątku przez większość czasu, wyrzuć wyjątek, aby zaoszczędzić głupoty ręcznego przekazywania go na stos.
- Jeśli nie masz pewności, co zrobi bezpośredni dzwoniący, może podać zarówno, jak
Parse
iTryParse
.
źródło
Result
;-)FileNotFoundException
.Either
OneOf
Odpowiedzi:
Z pewnością jest to dobra rzecz.
Chcesz, aby wszystko, co pozostawia system w nieokreślonym stanie, zatrzymało system, ponieważ niezdefiniowany system może robić okropne rzeczy, takie jak uszkodzone dane, formatować dysk twardy i wysyłać wiadomości e-mail do prezydenta. Jeśli nie możesz odzyskać systemu i przywrócić go do określonego stanu, przyczyną awarii jest awaria. Właśnie dlatego budujemy systemy, które powodują awarie, a nie cicho się rozrywają. Teraz pewnie wszyscy chcemy stabilnego systemu, który nigdy się nie zawiesza, ale naprawdę chcemy tego tylko wtedy, gdy system pozostaje w zdefiniowanym przewidywalnym bezpiecznym stanie.
To absolutnie prawda, ale często jest niezrozumiana. Kiedy wymyślili system wyjątków, bali się, że łamią programowanie strukturalne. Programowanie strukturalne dlatego mamy
for
,while
,until
,break
, icontinue
gdy wszystko, czego potrzebujemy, aby zrobić wszystko, że jestgoto
.Dijkstra nauczył nas, że używanie goto w sposób nieformalny (czyli skakanie w dowolne miejsce) sprawia, że czytanie kodu jest koszmarem. Kiedy dali nam system wyjątków, bali się, że wymyślają na nowo goto. Dlatego powiedzieli nam, aby nie „używać go do kontroli przepływu”, mając nadzieję, że zrozumiemy. Niestety wielu z nas tego nie zrobiło.
O dziwo, często nie nadużywamy wyjątków do tworzenia kodu spaghetti, jak to było w przypadku goto. Wydaje się, że sama rada spowodowała więcej problemów.
Zasadniczo wyjątki dotyczą odrzucenia założenia. Gdy poprosisz o zapisanie pliku, zakładasz, że plik może i zostanie zapisany. Wyjątek występuje, gdy nie może być to niezgodne z prawem, że HD jest pełny lub ponieważ szczur wgryzł się w kabel danych. Możesz obsłużyć wszystkie te błędy w różny sposób, możesz obsłużyć je wszystkie w ten sam sposób lub możesz pozwolić im zatrzymać system. W twoim kodzie jest szczęśliwa ścieżka, w której twoje założenia muszą być prawdziwe. Tak czy inaczej wyjątki zabierają cię z tej szczęśliwej ścieżki. Ściśle mówiąc, tak, to rodzaj „kontroli przepływu”, ale nie o to ci ostrzegali. Mówili o takich bzdurach :
„Wyjątki powinny być wyjątkowe”. Ta mała tautologia narodziła się, ponieważ projektanci systemów wyjątków potrzebują czasu na tworzenie śladów stosu. W porównaniu do skakania jest to powolne. Zjada czas procesora. Ale jeśli masz zamiar zalogować się i zatrzymać system lub przynajmniej zatrzymać bieżące intensywne przetwarzanie przed uruchomieniem następnego, masz trochę czasu na zabicie. Jeśli ludzie zaczną używać wyjątków „do kontroli przepływu”, te założenia dotyczące czasu znikną z okna. Tak więc „wyjątki powinny być wyjątkowe” zostały nam przekazane jako wzgląd na wydajność.
Znacznie ważniejsze niż to nas nie myli. Jak długo zajęło ci zauważenie nieskończonej pętli w powyższym kodzie?
... to dobra rada, gdy jesteś w bazie kodu, która zwykle nie używa kodów błędów. Dlaczego? Ponieważ nikt nie będzie pamiętać, aby zapisać wartość zwracaną i sprawdzić kody błędów. To wciąż dobra konwencja, gdy jesteś w C.
Używasz innej konwencji. W porządku, o ile ustanawiasz konwencję, a nie tylko walczysz z inną. Mylące są dwie konwencje błędów w tej samej bazie kodu. Jeśli w jakiś sposób pozbyłeś się całego kodu korzystającego z innej konwencji, to idź dalej.
Sam lubię konwencję. Jedno z najlepszych wyjaśnień, jakie tu znalazłem * :
Ale bardzo mi się podoba i nadal nie będę mieszał go z innymi konwencjami. Wybierz jeden i trzymaj się go. 1
1: Rozumiem przez to, że nie myślę o więcej niż jednej konwencji w tym samym czasie.
To naprawdę nie jest takie proste. Jedną z podstawowych rzeczy, które musisz zrozumieć, jest zero.
Ile dni pozostało w maju? 0 (ponieważ to nie jest maj. To już czerwiec).
Wyjątki są sposobem na odrzucenie założenia, ale nie są jedynym sposobem. Jeśli użyjesz wyjątków, aby odrzucić założenie, opuścisz szczęśliwą ścieżkę. Ale jeśli wybrałeś wartości, aby zesłać szczęśliwą ścieżkę, która sygnalizuje, że rzeczy nie są tak proste, jak zakładano, możesz pozostać na tej ścieżce, dopóki będzie ona w stanie poradzić sobie z tymi wartościami. Czasami 0 jest już używane, aby coś oznaczać, więc musisz znaleźć inną wartość, na którą odwzorujesz swój pomysł odrzucenia założenia. Możesz rozpoznać ten pomysł po jego użyciu w starej dobrej algebrze . Monady mogą w tym pomóc, ale nie zawsze musi to być monada.
Na przykład 2 :
Czy potrafisz wymyślić jakiś dobry powód, dla którego musi to być zaprojektowane tak, aby celowo rzucało cokolwiek? Zgadnij, co otrzymasz, gdy nie można przeanalizować żadnego elementu int? Nawet nie muszę ci mówić.
To znak dobrego imienia. Przepraszam, ale TryParse nie jest moim pomysłem na dobre imię.
Często unikamy rzucania wyjątku na nieotrzymanie niczego, gdy odpowiedź może być więcej niż jedną rzeczą w tym samym czasie, ale z jakiegoś powodu, jeśli odpowiedź jest albo jedna, albo niczym, mamy obsesję na punkcie nalegania, aby dać nam jedną rzecz lub rzucić:
Czy linie równoległe naprawdę muszą powodować tutaj wyjątek? Czy to naprawdę takie złe, jeśli ta lista nigdy nie będzie zawierać więcej niż jednego punktu?
Może semantycznie po prostu nie możesz tego znieść. Jeśli tak, szkoda. Ale może Monady, które nie mają tak dowolnych rozmiarów, jak
List
to, sprawią, że poczujesz się lepiej.Monady to małe kolekcje specjalnego przeznaczenia, które mają być używane w określony sposób, dzięki czemu nie trzeba ich testować. Powinniśmy znaleźć sposoby radzenia sobie z nimi bez względu na to, co zawierają. W ten sposób szczęśliwa ścieżka pozostaje prosta. Jeśli otworzysz się i przetestujesz każdą dotkniętą Monadę, używasz ich źle.
Wiem, to dziwne. Ale to nowe narzędzie (cóż, dla nas). Daj więc trochę czasu. Młotki mają większy sens, gdy przestajesz używać ich na śrubach.
Jeśli mi pozwolisz, chciałbym odpowiedzieć na ten komentarz:
To jest absolutna prawda. Monady są znacznie bliżej kolekcji niż wyjątki, flagi lub kody błędów. Robią dobre pojemniki na takie rzeczy, jeśli są mądrze używane.
źródło
throw
tak naprawdę nie wiem / nie mówimy, dokąd skaczemy;)C # to nie Haskell i powinieneś postępować zgodnie z konsensusem ekspertów społeczności C #. Jeśli zamiast tego spróbujesz zastosować praktyki Haskell w projekcie C #, zrazisz wszystkich w zespole, a ostatecznie prawdopodobnie odkryjesz powody, dla których społeczność C # robi różne rzeczy. Jednym z głównych powodów jest to, że C # nie obsługuje wygodnie dyskryminowanych związków.
To nie jest powszechnie akceptowana prawda. Wybór w każdym języku, który obsługuje wyjątki, to albo rzucić wyjątek (którego osoba dzwoniąca może nie obsłużyć), albo zwrócić pewną wartość złożoną (którą MUSI obsługiwać osoba dzwoniąca).
Propagowanie warunków błędu w górę przez stos wywołań wymaga warunku na każdym poziomie, podwajając cykliczność złożoności tych metod, aw konsekwencji podwajając liczbę przypadków testowych w jednostce. W typowych aplikacjach biznesowych wiele wyjątków nie można odzyskać i można je propagować na najwyższym poziomie (np. Punkt wejścia usługi aplikacji sieci web).
źródło
Przybyłeś do C # w interesującym czasie. Do niedawna język był mocno osadzony w imperatywnej przestrzeni programowania. Stosowanie wyjątków do komunikowania błędów było zdecydowanie normą. Używanie kodów zwrotnych cierpiało z powodu braku wsparcia językowego (np. Wokół dyskryminowanych związków i dopasowywania wzorców). Całkiem rozsądnie oficjalne wytyczne firmy Microsoft miały na celu unikanie kodów powrotu i stosowanie wyjątków.
Zawsze jednak istniały wyjątki w postaci
TryXXX
metod, któreboolean
zwracałyby wynik powodzenia i dostarczały wynik drugiej wartości poprzezout
parametr. Są one bardzo podobne do wzorców „wypróbowania” w programowaniu funkcjonalnym, z tym wyjątkiem, że wynik pochodzi z parametru wyjściowego, a nie zMaybe<T>
wartości zwracanej.Ale rzeczy się zmieniają. Programowanie funkcjonalne staje się jeszcze bardziej popularne, a języki takie jak C # reagują. C # 7.0 wprowadził pewne podstawowe funkcje dopasowania wzorców do języka; C # 8 wprowadzi znacznie więcej, w tym wyrażenie przełączające, wzorce rekurencyjne itp. Wraz z tym nastąpił rozwój „bibliotek funkcjonalnych” dla C #, takich jak moja biblioteka Succinc <T> , która zapewnia również wsparcie dla dyskryminowanych związków .
Te dwie rzeczy razem oznaczają, że kod podobny do następującego powoli zyskuje na popularności.
W tej chwili jest wciąż dość niszowa, chociaż nawet w ciągu ostatnich dwóch lat zauważyłem wyraźną zmianę z ogólnej wrogości wśród programistów C # na taki kod na rosnące zainteresowanie wykorzystaniem takich technik.
Nie mówimy jeszcze „ NIE zwracaj kodów błędów”. jest przestarzały i wymaga przejścia na emeryturę. Ale marsz drogą do tego celu jest już w toku. Wybierz więc: trzymaj się starych sposobów zgłaszania wyjątków przez gejowską rezygnację, jeśli tak lubisz robić rzeczy; lub zacznij odkrywać nowy świat, w którym konwencje z języków funkcjonalnych stają się coraz bardziej popularne w C #.
źródło
java.util.Stream
(podobnie jak IE półliczna ) i lambda, prawda? :)Wydaje mi się, że użycie DU do zwrócenia błędów jest całkowicie uzasadnione, ale powiedziałbym to tak, jak napisałem OneOf :) Ale powiedziałbym to tak, ponieważ jest to powszechna praktyka w F # itp. (Np. Typ wyniku, jak wspomniali inni ).
Nie myślę o błędach, które zwykle powracam, jako wyjątkowych sytuacjach, ale raczej jako normalnych, ale mam nadzieję, że rzadko spotykane sytuacje, które mogą lub nie muszą być obsługiwane, ale powinny być modelowane.
Oto przykład ze strony projektu.
Zgłaszanie wyjątków dla tych błędów wydaje się przesadą - mają one narzut związany z wydajnością, oprócz wad polegających na tym, że nie są wyraźne w sygnaturze metody lub zapewniają wyczerpujące dopasowanie.
W jakim stopniu OneOf jest idiomatyczny w c #, to kolejne pytanie. Składnia jest poprawna i względnie intuicyjna. DU i programowanie zorientowane na szyny są dobrze znanymi koncepcjami.
Zapisałbym wyjątki dla rzeczy, które wskazują, że coś jest zepsute tam, gdzie to możliwe.
źródło
OneOf
chodzi o to, aby wartość zwracana była bogatsza, jak zwracanie wartości Nullable. Zastępuje wyjątki w sytuacjach, które na początku nie są wyjątkowe.OneOf
jest mniej więcej równoważny sprawdzonym wyjątkom w Javie. Istnieją pewne różnice:OneOf
nie działa z metodami wywołującymi metody wywołujące ich skutki uboczne (ponieważ można je sensownie wywołać, a wynik po prostu zignorować). Oczywiście wszyscy powinniśmy starać się używać czystych funkcji, ale nie zawsze jest to możliwe.OneOf
nie zawiera informacji o tym, gdzie wystąpił problem. Jest to dobre, ponieważ pozwala zaoszczędzić wydajność (zgłoszone wyjątki w Javie są kosztowne ze względu na wypełnienie śladu stosu, aw języku C # będzie to samo) i zmusza do podania wystarczających informacji w elemencie błęduOneOf
. Jest to również złe, ponieważ informacje te mogą być niewystarczające i trudno jest znaleźć źródło problemu.OneOf
i zaznaczony wyjątek ma jedną wspólną „wspólną cechę”:To zapobiega twojemu strachowi
ale jak już powiedziano, strach ten jest irracjonalny. Zasadniczo w twoim programie jest zwykle jedno miejsce, w którym musisz wszystko złapać (istnieje szansa, że użyjesz już frameworka). Ponieważ Ty i Twój zespół spędzacie zwykle tygodnie lub lata pracując nad programem, nie zapomnicie go, prawda?
Dużą zaletą ignorowanych wyjątków jest to, że można je obsługiwać wszędzie tam, gdzie chcesz je obsłużyć, a nie wszędzie w śladzie stosu. To eliminuje masę bojlerów (wystarczy spojrzeć na niektóre kody Java deklarujące „rzuty…” lub owijanie wyjątków), a także sprawia, że kod jest mniej problematyczny: Jakakolwiek metoda może rzucić, musisz być tego świadomy. Na szczęście właściwa akcja zwykle nic nie robi , tzn. Pozwala bąbelkowi dotrzeć do miejsca, w którym można rozsądnie sobie z tym poradzić.
źródło
OneOf<SomeSuccess, SomeErrorInfo>
GdzieSomeErrorInfo
zawiera szczegółowe informacje o błędzie.Błędy mają wiele wymiarów. Identyfikuję trzy wymiary: czy można im zapobiec, jak często występują i czy można je odzyskać. Tymczasem sygnalizacja błędów ma głównie jeden wymiar: decydowanie, w jakim stopniu pokonać dzwoniącego nad głowę, aby zmusić go do obsługi błędu, lub pozwolić, aby błąd „cicho” wybuchł jako wyjątek.
Błędy, których nie można zapobiec, częste i możliwe do odzyskania błędy, to te, które naprawdę muszą zostać naprawione w miejscu połączenia. Najlepiej uzasadniają zmuszenie dzwoniącego do skonfrontowania się z nimi. W Javie sprawiam, że sprawdzają wyjątki, a podobny efekt uzyskuje się, stosując
Try*
metody lub zwracając dyskryminację unii. Zróżnicowane związki są szczególnie przydatne, gdy funkcja ma wartość zwracaną. Są znacznie bardziej dopracowaną wersją powrotunull
. Składniatry/catch
bloków nie jest świetna (zbyt wiele nawiasów), dzięki czemu alternatywy wyglądają lepiej. Ponadto wyjątki są nieco powolne, ponieważ rejestrują ślady stosu.Błędy, których można uniknąć (których nie można uniknąć z powodu błędu / zaniedbania programisty) i błędy niemożliwe do odzyskania działają naprawdę dobrze jako zwykłe wyjątki. Rzadkie błędy działają również lepiej niż zwykłe wyjątki, ponieważ programista często może ważyć, że nie warto tego przewidywać (zależy to od celu programu).
Ważną kwestią jest to, że często witryna użytkowania określa, w jaki sposób tryby błędów metody pasują do tych wymiarów, o czym należy pamiętać, rozważając następujące kwestie.
Z mojego doświadczenia wynika, że zmuszanie dzwoniącego do stawienia czoła warunkom błędu jest w porządku, gdy błędom nie można zapobiec, często i można je odzyskać, ale staje się bardzo denerwujące, gdy dzwoniący jest zmuszony przeskakiwać przez obręcze, gdy nie potrzebuje lub nie chce . Może to być spowodowane tym, że osoba dzwoniąca wie, że błąd się nie zdarzy, lub dlatego, że (jeszcze) nie chce, aby kod był odporny. W Javie wyjaśnia to niepokój związany z częstym używaniem sprawdzonych wyjątków i zwracaniem Opcjonalnego (który jest podobny do Być może i został niedawno wprowadzony z powodu wielu kontrowersji). W razie wątpliwości zgłaszaj wyjątki i pozwól dzwoniącemu zdecydować, w jaki sposób chcą je obsłużyć.
Na koniec należy pamiętać, że najważniejszą rzeczą w warunkach błędu, znacznie przewyższającą wszelkie inne czynniki, takie jak sposób ich sygnalizowania, jest dokładne ich udokumentowanie .
źródło
W innych odpowiedziach omówiono wyjątki kontra kody błędów wystarczająco szczegółowo, dlatego chciałbym dodać kolejną perspektywę specyficzną dla pytania:
OneOf nie jest kodem błędu, jest raczej monadą
Sposób, w jaki jest używany,
OneOf<Value, Error1, Error2>
jest kontenerem, który reprezentuje rzeczywisty wynik lub stan błędu. To jest takOptional
, z wyjątkiem tego, że gdy nie ma żadnej wartości, może podać więcej szczegółów, dlaczego tak jest.Głównym problemem z kodami błędów jest to, że zapominasz je sprawdzić. Ale tutaj dosłownie nie można uzyskać dostępu do wyniku bez sprawdzania błędów.
Jedynym problemem jest to, że nie dbasz o wynik. Przykład z dokumentacji OneOf podaje:
... a następnie tworzą różne odpowiedzi HTTP dla każdego wyniku, co jest znacznie lepsze niż stosowanie wyjątków. Ale jeśli wywołasz taką metodę.
wtedy nie ma problemu, a wyjątki byłoby lepszym wyborem. Porównaj Rail
save
VSsave!
.źródło
Jedną rzeczą, którą należy wziąć pod uwagę, jest to, że kod w języku C # zasadniczo nie jest czysty. W bazie kodu pełnej efektów ubocznych i przy kompilatorze, który nie dba o to, co robisz ze zwracaną wartością jakiejś funkcji, twoje podejście może wywołać znaczny smutek. Na przykład, jeśli masz metodę, która usuwa plik, chcesz się upewnić, że aplikacja powiadomi, gdy metoda się nie powiedzie, nawet jeśli nie ma powodu, aby sprawdzać kod błędu „na szczęśliwej ścieżce”. Jest to znacząca różnica w stosunku do języków funkcjonalnych, które wymagają jawnego zignorowania wartości zwrotnych, których nie używasz. W języku C # zwracane wartości są zawsze domyślnie ignorowane (jedynym wyjątkiem są moduły pobierające właściwości IIRC).
Powodem, dla którego MSDN mówi „nie zwracać kodów błędów” jest właśnie to - nikt nie zmusza cię nawet do odczytania kodu błędu. Kompilator wcale ci nie pomaga. Jednakże, jeśli czynność jest efektem ubocznym wolne, to można bezpiecznie używać coś takiego
Either
- chodzi o to, że nawet jeśli zignorować wynik błędu (jeśli w ogóle), można tylko zrobić, jeśli nie korzystać z „właściwego rezultatu” zarówno. Jest kilka sposobów, jak to osiągnąć - na przykład możesz zezwolić na „odczytanie” wartości, przekazując delegata do obsługi przypadków sukcesu i błędów, i możesz łatwo połączyć to z podejściami takimi jak programowanie zorientowane na kolej ( działa dobrze w C #).int.TryParse
jest gdzieś pośrodku. To jest czyste. Definiuje to, jakie są wartości dwóch wyników przez cały czas - więc wiesz, że jeśli zwracana jest wartośćfalse
, parametr wyjściowy zostanie ustawiony na0
. Nadal nie przeszkadza ci to w użyciu parametru wyjściowego bez sprawdzenia wartości zwracanej, ale przynajmniej masz gwarancję tego, jaki będzie wynik, nawet jeśli funkcja się nie powiedzie.Ale jedną absolutnie kluczową rzeczą jest tutaj konsekwencja . Kompilator nie uratuje cię, jeśli ktoś zmieni tę funkcję, aby mieć skutki uboczne. Lub rzucać wyjątki. Więc chociaż takie podejście jest w porządku w C # (i ja go używam), musisz upewnić się, że wszyscy w Twoim zespole go rozumieją i używają . Wiele niezmienników wymaganych do sprawnego programowania funkcjonalnego nie jest egzekwowanych przez kompilator C # i musisz upewnić się, że są one śledzone przez ciebie. Musisz zdawać sobie sprawę z tego, co się psuje, jeśli nie postępujesz zgodnie z regułami, które Haskell domyślnie egzekwuje - i jest to coś, o czym pamiętasz w przypadku wszelkich funkcjonalnych paradygmatów, które wprowadzasz do swojego kodu.
źródło
Poprawnym stwierdzeniem byłoby: Nie używaj kodów błędów jako wyjątków! I nie używaj wyjątków dla wyjątków.
Jeśli dzieje się coś, z czego Twój kod nie może się odzyskać (tzn. Sprawić, aby działał), jest to wyjątek. Nie ma nic, co twój bezpośredni kod zrobiłby z kodem błędu, poza przekazaniem go do góry, aby się zalogować lub być może w jakiś sposób przekazać kod użytkownikowi. Jednak właśnie po to są wyjątki - zajmują się całym przekazaniem i pomagają analizować, co się naprawdę wydarzyło.
Jeśli jednak zdarzy się coś, na przykład nieprawidłowe dane wejściowe, które można obsłużyć, albo automatycznie formatując je ponownie, albo prosząc użytkownika o poprawienie danych (i pomyślałeś o tym przypadku), to nie jest tak naprawdę wyjątkiem - tak jak ty spodziewać się tego. Możesz obsłużyć te przypadki z kodami błędów lub dowolną inną architekturą. Po prostu nie wyjątki.
(Zauważ, że nie mam dużego doświadczenia w języku C #, ale moja odpowiedź powinna dotyczyć ogólnego przypadku opartego na poziomie koncepcyjnym wyjątków).
źródło
catch
słowo kluczowe nie istnieje. A ponieważ rozmawiamy w kontekście C #, warto zauważyć, że w filozofii, której się tu przyświeca, nie stosuje się środowiska .NET; być może najprostszym możliwym do wyobrażenia przykładem „nieprawidłowych danych wejściowych, które można obsłużyć”, jest wpisanie przez użytkownika wartości innej niż liczba w polu, w którym spodziewana jest liczba… iint.Parse
zgłasza wyjątek.To „Straszny pomysł”.
Jest to okropne, ponieważ przynajmniej w .net nie ma wyczerpującej listy możliwych wyjątków. Każdy kod może generować dowolny wyjątek. Zwłaszcza jeśli robisz OOP i możesz wywoływać przesłonięte metody na podtypie, a nie na typie zadeklarowanym
Musisz więc zmienić wszystkie metody i funkcje na
OneOf<Exception, ReturnType>
, a następnie, jeśli chcesz obsługiwać różne typy wyjątków, musisz sprawdzić typIf(exception is FileNotFoundException)
itp.try catch
jest odpowiednikiemOneOf<Exception, ReturnType>
Edytować ----
Wiem, że proponujesz powrót,
OneOf<ErrorEnum, ReturnType>
ale wydaje mi się, że jest to różnica bez różnicy.Łączymy kody powrotu, oczekiwane wyjątki i rozgałęzienia według wyjątków. Ale ogólny efekt jest taki sam.
źródło
Exception
.