zachowując kod mojego kolegi nawet przed kimś, kto twierdzi, że jest starszym programistą, często widzę następujący kod:
try
{
//do something
}
catch
{
//Do nothing
}
lub czasami zapisują informacje rejestrujące do plików dziennika, takich jak następujący try catch
blok
try
{
//do some work
}
catch(Exception exception)
{
WriteException2LogFile(exception);
}
Zastanawiam się tylko, czy to, co zrobili, jest najlepszą praktyką? To wprawia mnie w zakłopotanie, ponieważ w moim myśleniu użytkownicy powinni wiedzieć, co dzieje się z systemem.
Proszę o radę.
NullReference
lubArgumentNull
nie jest to część przepływu aplikacji), oznacza to, że istnieje błąd, który należy naprawić, więc jego zarejestrowanie pomoże znacznie szybciej debugować kod.Odpowiedzi:
Moja strategia obsługi wyjątków to:
Aby złapać wszystkie nieobsługiwane wyjątki, przechwytując do
Application.ThreadException event
, a następnie zdecyduj:Następnie zawsze dołączam każdy fragment kodu uruchamiany zewnętrznie w
try/catch
:Następnie zamykam w „try / catch”
ApplicationException("custom message", innerException)
aby śledzić, co naprawdę się wydarzyłoPonadto staram się jak najlepiej sortować wyjątki . Istnieją wyjątki, które:
finally
sekcji podczasTreeView
wypełniania)użytkownik nie dba o to, ale ważne jest, aby wiedzieć, co się stało. Więc zawsze je loguję:
Dobrą praktyką jest projektowanie niektórych statycznych metod obsługi wyjątków w modułach obsługi błędów najwyższego poziomu aplikacji.
Zmuszam się także do spróbowania:
Wreszcie:
Zły:
Bezużyteczny:
Próba wreszcie bez połowu jest całkowicie ważna:
Co robię na najwyższym poziomie:
Co robię w niektórych nazwanych funkcjach:
Ma wiele wspólnego z obsługą wyjątków (wyjątki niestandardowe), ale te reguły, o których staram się pamiętać, wystarczają do prostych aplikacji, które robię.
Oto przykład metod rozszerzeń do wygodnej obsługi przechwyconych wyjątków. Są one zaimplementowane w taki sposób, że można je łączyć ze sobą i bardzo łatwo jest dodać własne przetwarzanie wyjątków.
źródło
catch(Exception ex) { throw ex; }
w C # jest gorszy niż redundantny (niezależnie od rodzaju wyjątku, który wychwytujesz). Aby ponownie rzucić, użyjthrow;
. W pierwszym przypadku wyjątek będzie wyglądał, jakby pochodził z twojego,throw ex
podczas gdy w drugim przypadku będzie poprawnie pochodzić z oryginalnegothrow
stwierdzenia.Application.ThreadException
wydarzenie i otaczasz każdy wyjątek znakiemcatch(Exception ex) {ex.Log(ex);}
. Prawdopodobnie zgodziłbym się, że ta pierwsza jest doskonałą praktyką, ale druga zwiększa ryzyko powielenia dzienników błędów i ukrywa, że wystąpił wyjątek.throw ex
Jest też bardzo, bardzo źle.Application.ThreadException
wydarzenie nie zdawałem sobie z tego sprawy, bardzo przydatne.Najlepszą praktyką jest to, że obsługa wyjątków nigdy nie powinna ukrywać problemów . Oznacza to, że
try-catch
bloki powinny być niezwykle rzadkie.Istnieją 3 okoliczności, w których użycie
try-catch
sensu ma sens.Zawsze radzi sobie ze znanymi wyjątkami tak nisko, jak to tylko możliwe. Jeśli jednak oczekujesz wyjątku, zwykle lepiej jest najpierw go przetestować. Na przykład parsowanie, formatowanie i wyjątki arytmetyczne są prawie zawsze lepiej obsługiwane przez kontrole logiczne, a nie określone
try-catch
.Jeśli musisz zrobić coś na podstawie wyjątku (na przykład rejestrować lub wycofać transakcję), ponownie wyrzuć wyjątek.
Zawsze zajmuj się nieznanymi wyjątkami tak wysoko, jak to tylko możliwe - jedynym kodem, który powinien wykorzystywać wyjątek, a nie ponownie go rzucać, powinien być interfejs użytkownika lub publiczny interfejs API.
Załóżmy, że łączysz się ze zdalnym interfejsem API, tutaj możesz spodziewać się pewnych błędów (i mieć coś takiego w takich okolicznościach), więc jest to przypadek 1:
Pamiętaj, że nie są wychwytywane żadne inne wyjątki, ponieważ nie są one oczekiwane.
Załóżmy teraz, że próbujesz zapisać coś w bazie danych. Musimy go wycofać, jeśli się nie powiedzie, więc mamy przypadek 2:
Pamiętaj, że ponownie rzucamy wyjątek - kod wyżej musi wiedzieć, że coś się nie udało.
Wreszcie mamy interfejs użytkownika - tutaj nie chcemy mieć całkowicie nieobsługiwanych wyjątków, ale nie chcemy ich również ukrywać. Oto przykład przypadku 3:
Jednak większość platform API lub interfejsów użytkownika ma ogólne sposoby wykonywania przypadku 3. Na przykład ASP.Net ma żółty ekran błędu, który zrzuca szczegóły wyjątku, ale można go zastąpić bardziej ogólnym komunikatem w środowisku produkcyjnym. Przestrzeganie tych zasad jest najlepszą praktyką, ponieważ pozwala zaoszczędzić dużo kodu, ale także dlatego, że rejestrowanie błędów i wyświetlanie powinny być decyzjami konfiguracyjnymi, a nie zakodowanymi na stałe.
To wszystko oznacza, że zarówno przypadek 1 (znane wyjątki), jak i przypadek 3 (jednorazowa obsługa interfejsu użytkownika) mają lepsze wzorce (unikaj oczekiwanego błędu lub obsługi błędów w interfejsie użytkownika).
Nawet przypadek 2 można zastąpić lepszymi wzorcami, na przykład zakresy transakcji (
using
bloki, które wycofują każdą transakcję niezatwierdzoną podczas bloku) utrudniają programistom błędne wzorce najlepszych praktyk.Załóżmy na przykład, że masz aplikację ASP.Net na dużą skalę. Rejestrowanie błędów może odbywać się za pośrednictwem ELMAH , wyświetlanie błędów może być pouczającym lokalnym YSoD i ładnym zlokalizowanym komunikatem w produkcji. Połączenia z bazą danych mogą odbywać się za pośrednictwem zakresów transakcji i
using
bloków. Nie potrzebujesz ani jednegotry-catch
bloku.TL; DR: Najlepszą praktyką jest w ogóle nie używanie
try-catch
bloków.źródło
try-catch
- może (bardzo sporadycznie) być przydatny i nie twierdzę, że nigdy nie powinieneś ich używać, ale 99% przypadków jest lepszy sposób.Wyjątkiem jest błąd blokowania .
Przede wszystkim najlepszą praktyką nie powinno być rzucanie wyjątków dla jakiegokolwiek rodzaju błędu, chyba że jest to błąd blokujący .
Jeśli błąd jest blokowany , wyrzuć wyjątek. Po zgłoszeniu wyjątku nie trzeba go ukrywać, ponieważ jest wyjątkowy; poinformuj użytkownika o tym (należy sformatować cały wyjątek do czegoś przydatnego dla użytkownika w interfejsie użytkownika).
Twoim zadaniem jako programisty jest zapobieganie wyjątkowemu przypadkowi, w którym niektóre parametry lub sytuacja w środowisku wykonawczym mogą zakończyć się wyjątkiem. Oznacza to, że wyjątków nie można tłumić, ale należy ich unikać .
Na przykład, jeśli wiesz, że niektóre liczby całkowite mogą mieć niepoprawny format, użyj
int.TryParse
zamiastint.Parse
. Istnieje wiele przypadków, w których możesz to zrobić zamiast po prostu powiedzieć „jeśli to się nie powiedzie, po prostu wyrzuć wyjątek”.Zgłaszanie wyjątków jest drogie.
Jeśli w końcu zostanie zgłoszony wyjątek, zamiast zapisywać wyjątek w dzienniku po jego zgłoszeniu, jedną z najlepszych praktyk jest łapanie go w module obsługi wyjątków pierwszej szansy . Na przykład:
Moje stanowisko jest takie, że lokalne try / catch są lepiej dostosowane do obsługi specjalnych przypadków, w których możesz przetłumaczyć wyjątek na inny lub gdy chcesz „wyciszyć” go w bardzo, bardzo, bardzo, bardzo, bardzo szczególnym przypadku (błąd biblioteki zgłoszenie niepowiązanego wyjątku, który musisz wyciszyć, aby obejść cały błąd).
W pozostałych przypadkach:
Odpowiadanie na @thewhiteambit w sprawie komentarza ...
@thewhiteambit powiedział:
Po pierwsze, jak wyjątek nie może być nawet błędem?
null
podczas spodziewanego obiektu => wyjątekMożemy wymienić 1k przypadków, gdy zostanie zgłoszony wyjątek, a przecież każdy z możliwych przypadków będzie błędem .
Wyjątkiem jest błąd, ponieważ pod koniec dnia jest to obiekt, który zbiera informacje diagnostyczne - ma komunikat i zdarza się, gdy coś pójdzie nie tak.
Nikt nie rzuci wyjątku, gdy nie ma wyjątkowego przypadku. Wyjątkami powinny być błędy blokowania, ponieważ po ich zgłoszeniu , jeśli nie spróbujesz wpaść w użycie try / catch, a wyjątki w celu wdrożenia przepływu sterowania oznaczają, że Twoja aplikacja / usługa zatrzyma operację, która wystąpiła w wyjątkowym przypadku .
Ponadto sugeruję wszystkim, aby sprawdzili niezawodny paradygmat opublikowany przez Martina Fowlera (i napisany przez Jima Shore'a) . W ten sposób zawsze rozumiałem, jak radzić sobie z wyjątkami, nawet zanim dotarłem do tego dokumentu jakiś czas temu.
Zwykle wyjątki ograniczają przepływ operacji i są przetwarzane w celu przekształcenia ich w błędy zrozumiałe dla człowieka. Wydaje się więc, że wyjątek jest w rzeczywistości lepszym paradygmatem do obsługi przypadków błędów i pracy nad nimi, aby uniknąć awarii aplikacji / usługi i powiadomić użytkownika / konsumenta, że coś poszło nie tak.
Więcej odpowiedzi na temat problemów @whiteambit
Jeśli aplikacja może działać w trybie offline bez utrwalania danych w bazie danych, nie należy używać wyjątków , ponieważ implementacja przepływu kontroli
try/catch
jest traktowana jako anty-wzorzec. Praca w trybie offline jest możliwym przypadkiem użycia, więc wdrażasz przepływ kontrolny, aby sprawdzić, czy baza danych jest dostępna, czy nie, nie czekasz, aż będzie nieosiągalna .Parsowanie rzeczą jest również oczekiwany przypadek ( nie przypadek wyjątkowy ). Jeśli się tego spodziewasz, nie używasz wyjątków, aby kontrolować przepływ! . Otrzymujesz od użytkownika trochę metadanych, aby wiedzieć, jaka jest jego kultura, i używasz do tego formatów! .NET obsługuje także to i inne środowiska oraz wyjątek, ponieważ należy unikać formatowania liczb, jeśli oczekuje się zastosowania aplikacji / usługi specyficznego dla kultury .
Ten artykuł jest tylko opinią lub punktem widzenia autora.
Ponieważ Wikipedia może być również opinią autora lub autorów artykułu, nie powiedziałbym, że to dogmat , ale sprawdź, co mówi artykuł Kodowanie według wyjątku gdzieś w jakimś akapicie:
Mówi także gdzieś:
Niepoprawne użycie wyjątku
Szczerze mówiąc, uważam, że nie można opracować oprogramowania, nie traktując poważnie przypadków użycia. Jeśli wiesz, że ...
... nie użyjesz do tego wyjątków . Można by wspierać tych przypadków użycia za pomocą regularnych kontroli przepływu.
A jeśli jakiś nieoczekiwany przypadek użycia nie zostanie uwzględniony, Twój kod szybko zawiedzie, ponieważ spowoduje zgłoszenie wyjątku . Racja, ponieważ wyjątek jest wyjątkowym przypadkiem .
Z drugiej strony, wreszcie, czasami obejmuje się wyjątkowe przypadki, zgłaszając oczekiwane wyjątki , ale nie wyrzuca się ich w celu wdrożenia przepływu kontrolnego. Robisz to, ponieważ chcesz powiadomić górne warstwy, że nie obsługujesz niektórych przypadków użycia lub twój kod nie działa z niektórymi argumentami lub danymi / właściwościami środowiska.
źródło
Jedyny raz, kiedy powinieneś martwić się o coś, co wydarzyło się w kodzie, jest to, że jest coś, co mogą lub muszą zrobić, aby uniknąć tego problemu. Jeśli mogą zmienić dane w formularzu, naciśnij przycisk lub zmień ustawienia aplikacji, aby uniknąć problemu, poinformuj ich o tym. Ale ostrzeżenia lub błędy, których użytkownik nie jest w stanie uniknąć, po prostu powodują, że tracą zaufanie do produktu.
Wyjątki i dzienniki są przeznaczone dla Ciebie, programisty, a nie dla użytkownika końcowego. Zrozumienie, co należy zrobić, gdy złapiesz każdy wyjątek, jest znacznie lepsze niż zastosowanie złotej reguły lub poleganie na siatce bezpieczeństwa obejmującej całą aplikację.
Bezmyślne kodowanie jest JEDYNYM rodzajem złego kodowania. Fakt, że czujesz, że jest coś lepszego, co można zrobić w takich sytuacjach, pokazuje, że jesteś zainwestowany w dobre kodowanie, ale unikaj prób stawiania pewnych ogólnych zasad w tych sytuacjach i rozumiesz powód, dla którego coś rzuca się na pierwszym miejscu i co możesz zrobić, aby się z niego zregenerować.
źródło
Wiem, że to stare pytanie, ale nikt tutaj nie wspomniał o artykule MSDN i to właśnie ten dokument mnie wyjaśnił, MSDN ma bardzo dobry dokument na ten temat, powinieneś wychwycić wyjątki, gdy spełnione są następujące warunki:
Proponuję przeczytać całą sekcję „ Wyjątki i obsługa wyjątków ”, a także Najlepsze praktyki dotyczące wyjątków .
źródło
Lepszym podejściem jest drugie (to, w którym określasz typ wyjątku). Zaletą tego jest to, że wiesz, że ten typ wyjątku może wystąpić w twoim kodzie. Obsługujesz ten typ wyjątku i możesz wznowić. Jeśli wystąpi inny wyjątek, oznacza to, że coś jest nie tak, co pomoże ci znaleźć błędy w kodzie. Aplikacja ostatecznie ulegnie awarii, ale dowiesz się, że coś przegapiłeś (błąd), który należy naprawić.
źródło
Z wyjątkami próbuję:
Najpierw wychwytuję specjalne typy wyjątków, takie jak dzielenie przez zero, operacje IO itd. I piszę zgodnie z tym kod. Na przykład, dzielenie przez zero, w zależności od proweniencji wartości, które mógłbym ostrzec użytkownika (przykład prostego kalkulatora w tym, że w środkowym obliczeniu (nie w argumentach) pojawia się w dzieleniu przez zero) lub w celu cichego potraktowania tego wyjątku, rejestrowanie i kontynuuj przetwarzanie.
Następnie próbuję wyłapać pozostałe wyjątki i zapisać je. Jeśli to możliwe, zezwól na wykonanie kodu, w przeciwnym razie powiadom użytkownika, że wystąpił błąd, i poproś go o przesłanie raportu o błędzie.
W kodzie coś takiego:
źródło
0
wcześniej licznik, a nie atry-catch
. Także po co łapaćException
tutaj ogólny ? Lepiej jest pozwolić, aby błąd się wzmógł, niż radzić sobie z nim tutaj we wszystkich przypadkach, w których się go nie spodziewasz.Drugie podejście jest dobre.
Jeśli nie chcesz pokazywać błędu i mylić użytkownika aplikacji, pokazując wyjątek czasu wykonywania (tj. Błąd), który nie jest z nim związany, po prostu zaloguj błąd, a zespół techniczny może poszukać problemu i go rozwiązać.
Polecam wybrać drugie podejście do całej aplikacji.
źródło
catch
bloki powinny zawsze wywoływaćthrow
wyjątek na górze lub zwracać coś / wyświetlać coś, co mówi użytkownikowi, że akcja się nie powiodła. Chcesz otrzymać pomoc techniczną, gdy nie uda się jej zapisać, a nie 6 miesięcy później, gdy spróbują ją odzyskać i nie będą w stanie jej znaleźć.Najgorszą rzeczą jest pozostawienie pustego bloku catch. Jeśli wystąpi błąd, najlepszym sposobem na jego rozwiązanie jest:
źródło
Dla mnie obsługę wyjątku można traktować jako regułę biznesową. Oczywiście pierwsze podejście jest niedopuszczalne. Drugi jest lepszy i może być w 100% poprawny, JEŚLI kontekst mówi tak. Teraz na przykład opracowujesz dodatek do programu Outlook. Jeśli dodasz zgłasza nieobsługiwany wyjątek, użytkownik programu Outlook może go teraz znać, ponieważ program Outlook nie zniszczy się z powodu awarii jednej wtyczki. I trudno ci zrozumieć, co poszło nie tak. Dlatego drugie podejście w tym przypadku jest dla mnie prawidłowe. Oprócz rejestrowania wyjątku możesz zdecydować o wyświetleniu użytkownikowi komunikatu o błędzie - uważam to za regułę biznesową.
źródło
Najlepszą praktyką jest zgłoszenie wyjątku, gdy wystąpi błąd. Ponieważ wystąpił błąd i nie należy go ukrywać.
Ale w prawdziwym życiu możesz mieć kilka sytuacji, gdy chcesz to ukryć
źródło
Exception
. Zawsze. Rzuć odpowiednią podklasę,Exception
jakiej chcesz, ale nigdy,Exception
ponieważ nie daje to absolutnie żadnych informacji semantycznych. Całkowicie nie widzę scenariusza, w którym sensowne jest rzucanie,Exception
ale nie jego podklasa.Należy wziąć pod uwagę niniejsze wytyczne projektowe dotyczące wyjątków
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions
źródło
catch
Bez argumentów jest po prostu jedzenie wyjątek i jest bezużyteczna. Co się stanie, jeśli wystąpi błąd krytyczny? Nie można wiedzieć, co się stanie, jeśli użyjesz catch bez argumentów.Instrukcja catch powinna wychwycić bardziej szczegółowe wyjątki, takie jak,
FileNotFoundException
a następnie na samym końcu powinieneś złapać,Exception
który przechwyciłby każdy inny wyjątek i zarejestrowałby je.źródło
catch(Exception)
na końcu? Jeśli się tego nie spodziewasz, zawsze najlepszym rozwiązaniem jest przekazanie go do następnej warstwy.Czasami musisz traktować wyjątki, które nic nie mówią użytkownikom.
Moja droga to:
To zdecydowanie nie musi być najlepsza praktyka. ;-)
źródło
Mogę ci coś powiedzieć:
Fragment nr 1 jest niedopuszczalny, ponieważ ignoruje wyjątek. (połyka, jakby nic się nie wydarzyło).
Więc nie dodawaj bloku catch, który nic nie robi lub po prostu cofa.
Blok połowowy powinien dodać pewną wartość. Na przykład komunikat wyjściowy do użytkownika końcowego lub błąd dziennika.
Nie należy używać wyjątku dla normalnej logiki programu przepływu. Na przykład:
np. sprawdzanie poprawności danych wejściowych. <- To nie jest poprawna wyjątkowa sytuacja, raczej powinieneś napisać metodę,
IsValid(myInput);
aby sprawdzić, czy element wejściowy jest prawidłowy, czy nie.Zaprojektuj kod, aby uniknąć wyjątku. Na przykład:
Jeśli przekażemy wartość, której nie można przeanalizować jako int, ta metoda wygeneruje wyjątek i wyjątek, zamiast tego możemy napisać coś takiego:
bool TryParse(string input,out int result);
<- ta metoda zwróci wartość logiczną wskazującą, czy parsowanie zakończyło się powodzeniem.Być może jest to trochę poza zakresem tego pytania, ale mam nadzieję, że pomoże to w podejmowaniu właściwych decyzji, jeśli chodzi o
try {} catch(){}
wyjątki.źródło