Czytałem porady dotyczące tego pytania, w jaki sposób należy traktować wyjątek jak najbliżej miejsca, w którym został zgłoszony.
Moim dylematem dotyczącym najlepszej praktyki jest to, czy należy użyć try / catch / wreszcie, aby zwrócić enum (lub liczbę całkowitą reprezentującą wartość, 0 dla błędu, 1 dla ok, 2 dla ostrzeżenia itp., W zależności od przypadku) , aby odpowiedź jest zawsze w porządku, czy też należy pozwolić, aby wyjątek przeszedł, aby część wywołująca sobie z tym poradziła?
Z tego, co mogę zebrać, może się to różnić w zależności od przypadku, więc oryginalna rada wydaje się dziwna.
Na przykład w usłudze sieciowej zawsze chciałbyś oczywiście przywrócić stan, więc każdy wyjątek musi zostać rozwiązany na miejscu, ale powiedzmy, że wewnątrz funkcji wysyłającej / pobierającej dane przez http, chciałbyś wyjątek (na przykład w przypadku 404), aby przejść tylko do tego, który go zwolnił. Jeśli tego nie zrobisz, musisz stworzyć jakiś sposób na poinformowanie strony wywołującej o jakości wyniku (błąd: 404), a także o samym wyniku.
Chociaż można spróbować złapać wyjątek 404 wewnątrz funkcji pomocnika, która pobiera / wysyła dane, prawda? Czy tylko ja używam smallinta do oznaczania stanów w programie (i oczywiście odpowiednio je dokumentuję), a następnie używam tych informacji do celów sprawdzania poprawności poczytalności (wszystko ok / obsługa błędów) na zewnątrz?
Aktualizacja: spodziewałem się fatalnego / nieśmiertelnego wyjątku dla głównej klasyfikacji, ale nie chciałem tego uwzględniać, aby nie przesądzać o odpowiedziach. Pozwól, że wyjaśnię, o co chodzi w tym pytaniu: obsługa zgłoszonych wyjątków, a nie zgłaszanie wyjątków. Jaki jest pożądany efekt: Wykryj błąd i spróbuj go naprawić. Jeśli odzyskanie nie jest możliwe, przekaż najbardziej znaczące informacje zwrotne.
Ponownie, w przykładzie http get / post, pytanie brzmi: czy powinieneś podać nowy obiekt, który opisuje, co się stało z pierwotnym dzwoniącym? Jeśli ten pomocnik był w bibliotece, której używasz, czy spodziewałbyś się, że dostarczy ci kod statusu operacji, czy też umieściłby go w bloku try-catch? Jeśli projektujesz go, czy podasz kod statusu, czy zgłaszasz wyjątek i czy zamiast tego pozwalasz wyższemu poziomowi przetłumaczyć go na kod / wiadomość statusu?
Streszczenie: Jak wybrałeś, jeśli kawałek kodu zamiast generować wyjątek, zwraca kod statusu wraz z wszelkimi wynikami, które może przynieść?
źródło
Odpowiedzi:
W wyjątkowych warunkach należy zastosować wyjątki. Zgłoszenie wyjątku polega zasadniczo na stwierdzeniu: „Nie mogę tutaj poradzić sobie z tym warunkiem; czy ktoś znajdujący się wyżej na stosie wywołań może to dla mnie złapać i obsłużyć?”
Zwracanie wartości może być preferowane, jeśli jest jasne, że osoba dzwoniąca weźmie tę wartość i zrobi z nią coś znaczącego. Jest to szczególnie prawdziwe, jeśli zgłoszenie wyjątku ma wpływ na wydajność, tzn. Może wystąpić w ciasnej pętli. Zgłoszenie wyjątku zajmuje znacznie więcej czasu niż zwrócenie wartości (o co najmniej dwa rzędy wielkości).
Wyjątków nie należy nigdy wykorzystywać do implementacji logiki programu. Innymi słowy, nie rzucaj wyjątku, aby coś zrobić; zgłosić wyjątek, aby stwierdzić, że nie można tego zrobić.
źródło
Pewna dobra rada, którą kiedyś przeczytałem, to: rzucaj wyjątki, gdy nie możesz robić postępów, biorąc pod uwagę stan danych, z którymi masz do czynienia, jednak jeśli masz metodę, która może zgłosić wyjątek, podaj także, w miarę możliwości, metodę stwierdzenia, czy dane są rzeczywiście ważne przed wywołaniem metody
Na przykład System.IO.File.OpenRead () wyrzuci wyjątek FileNotFoundException, jeśli podany plik nie istnieje, jednak zapewnia również metodę .Exists (), która zwraca wartość logiczną wskazującą, czy plik jest obecny, którą należy wywołać wcześniej wywołanie OpenRead (), aby uniknąć nieoczekiwanych wyjątków.
Aby odpowiedzieć na pytanie „kiedy powinienem poradzić sobie z wyjątkiem”, powiedziałbym, gdziekolwiek można coś z tym zrobić. Jeśli twoja metoda nie może poradzić sobie z wyjątkiem zgłaszanym przez metodę, którą wywołuje, nie wychwytuj jej. Niech podniesie się w górę łańcucha połączeń do czegoś, co może sobie z tym poradzić. W niektórych przypadkach może to być tylko rejestrator nasłuchujący wyjątku Application.UnhandledException.
źródło
To okropny projekt. Nie „maskuj” wyjątku, tłumacząc na kod numeryczny. Pozostaw to jako właściwy, jednoznaczny wyjątek.
Od tego są wyjątki.
To wcale nie jest uniwersalna prawda. Czasami to dobry pomysł. Innym razem nie jest to tak pomocne.
źródło
if
instrukcji zamiast (czasem) prostszej obsługi wyjątków. Odpowiedź („Jak wybrać ...”) jest prosta. Nie robić . Myślę, że powiedzenie tego dodaje się do rozmowy. Możesz jednak zignorować tę odpowiedź.Zgadzam się z S.Lott . Złapanie wyjątku jak najbliżej źródła może być dobrym lub złym pomysłem, w zależności od sytuacji. Kluczem do obsługi wyjątków jest złapanie ich tylko wtedy, gdy możesz coś z tym zrobić. Złapanie ich i zwrócenie wartości liczbowej funkcji wywołującej jest ogólnie złym projektem. Po prostu chcesz pozwolić im unosić się w górze, aż będziesz w stanie odzyskać.
Zawsze uważam obsługę wyjątków za krok od mojej logiki aplikacji. Zadaję sobie pytanie: czy ten wyjątek zostanie zgłoszony, jak daleko wstecz stosu wywołań muszę przeszukiwać, zanim moja aplikacja będzie w stanie do odzyskania? W wielu przypadkach, jeśli w aplikacji nie mogę nic zrobić, aby odzyskać, może to oznaczać, że nie łapię tego aż do najwyższego poziomu i po prostu rejestruję wyjątek, nie udaje mi się zadanie i staram się całkowicie zamknąć.
Naprawdę nie ma twardej i szybkiej reguły określającej, kiedy i jak skonfigurować obsługę wyjątków, niż pozostawienie ich w spokoju, dopóki nie będziesz wiedział, co z nimi zrobić.
źródło
Wyjątki to piękne rzeczy. Pozwalają one na przedstawienie jasnego opisu problemu związanego z czasem wykonywania bez niepotrzebnych dwuznaczności. Wyjątki mogą być wpisywane, podtypulowane i mogą być obsługiwane według typu. Można je przekazywać do obsługi w innym miejscu, a jeśli nie można ich obsłużyć, można je ponownie podnieść, aby zająć się wyższą warstwą w aplikacji. Będą również automatycznie wracać z Twojej metody bez konieczności przywoływania wielu szalonych elementów logicznych, aby radzić sobie z zaciemnionymi kodami błędów.
Powinieneś zgłosić wyjątek natychmiast po napotkaniu nieprawidłowych danych w kodzie. Powinieneś zawinąć wywołania innych metod w try..catch..full, aby obsłużyć wszelkie wyjątki, które mogą zostać zgłoszone, a jeśli nie wiesz, jak zareagować na dany wyjątek, wyrzuć go ponownie, aby wskazać wyższym warstwom, że jest coś złego, co powinno być rozwiązane gdzie indziej.
Zarządzanie kodami błędów może być bardzo trudne. Zwykle kończy się to niepotrzebnym powielaniem w kodzie i / lub dużą ilością niechlujnej logiki do radzenia sobie ze stanami błędów. Bycie użytkownikiem i napotkanie kodu błędu jest jeszcze gorsze, ponieważ sam kod nie będzie znaczący i nie zapewni użytkownikowi kontekstu błędu. Z drugiej strony wyjątek może dać użytkownikowi coś pożytecznego, na przykład „zapomniałeś wpisać wartość” lub „wprowadziłeś niepoprawną wartość, oto prawidłowy zakres, którego możesz użyć ...” lub „nie wiedz, co się stało, skontaktuj się z pomocą techniczną i powiedz im, że właśnie się zawiesiłem, i podaj im następujący ślad stosu ... ”.
Więc moje pytanie do PO brzmi: dlaczego na ziemi NIE chcesz używać wyjątków w stosunku do zwracania kodów błędów?
źródło
Wszystkie dobre odpowiedzi. Chciałbym również dodać, że zwracanie kodu błędu zamiast zgłaszania wyjątku może skomplikować kod osoby dzwoniącej. Powiedz metodę A wywołuje metodę B wywołuje metodę C i C napotyka błąd. Jeśli C zwraca kod błędu, teraz B musi mieć logikę, aby ustalić, czy może obsłużyć ten kod błędu. Jeśli nie może, to należy zwrócić go do A. Jeśli A nie może obsłużyć błędu, to co robisz? Rzuć wyjątek? W tym przykładzie kod jest znacznie bardziej przejrzysty, jeśli C po prostu zgłasza wyjątek, B nie przechwytuje wyjątku, więc automatycznie przerywa bez dodatkowego kodu potrzebnego do tego, a A może wychwycić pewne rodzaje wyjątków, pozwalając innym kontynuować połączenie stos.
Przypomina to dobrą zasadę kodowania poprzez:
źródło
Użyłem kombinacji obu rozwiązań: dla każdej funkcji sprawdzania poprawności przekazuję rekord, który wypełniam statusem sprawdzania poprawności (kod błędu). Na końcu funkcji, jeśli istnieje błąd sprawdzania poprawności, generuję wyjątek, w ten sposób nie generuję wyjątku dla każdego pola, ale tylko raz.
Skorzystałem również z tego, że zgłoszenie wyjątku zatrzyma wykonanie, ponieważ nie chcę, aby wykonywanie było kontynuowane, gdy dane są nieprawidłowe.
Na przykład
Jeśli polegasz tylko na wartości logicznej, programista korzystający z mojej funkcji powinien wziąć to pod uwagę pisząc:
ale może zapomnieć i tylko zadzwonić
Validate()
(wiem, że nie powinien, ale może mógłby).Tak więc w powyższym kodzie uzyskałem dwie zalety:
źródło
Nie ma tu jednej odpowiedzi - jakby nie było jednego rodzaju HttpException.
Wydaje się sensowne, że bazowe biblioteki HTTP zgłaszają wyjątek, gdy otrzymują odpowiedź 4xx lub 5xx; ostatnim razem, gdy spojrzałem na specyfikacje HTTP, były to błędy.
Jeśli chodzi o zgłaszanie wyjątku - lub pakowanie go i ponowne rzucanie - myślę, że tak naprawdę chodzi o przypadek użycia. Na przykład, jeśli piszesz opakowanie, aby pobrać niektóre dane z interfejsu API i udostępnić je aplikacjom, możesz zdecydować, że semantycznie żądanie nieistniejącego zasobu, który zwraca HTTP 404, ma sens, aby je złapać i zwrócić wartość null. Z drugiej strony błąd 406 (niedopuszczalny) może być warty zgłoszenia błędu, ponieważ oznacza to, że coś się zmieniło, a aplikacja powinna się zawieszać, nagrywać i krzyczeć o pomoc.
źródło