Dlaczego nie powinienem zawijać każdego bloku w „try” - „catch”?

430

Zawsze uważałem, że jeśli metoda może zgłosić wyjątek, nierozsądnie jest nie chronić tego wywołania za pomocą znaczącego bloku try.

Właśnie napisałem: „ ZAWSZE należy zawijać połączenia, które mogą próbować łapać bloki. „na to pytanie i powiedziano mi, że była to„ wyjątkowo zła rada ”- chciałbym zrozumieć, dlaczego.

Konrad
źródło
4
Gdybym wiedział, że metoda zgłosi wyjątek, poprawiłbym go w pierwszej kolejności. Ponieważ nie wiem gdzie i dlaczego kod wyrzuci wyjątki, łapię je u źródła (każda metoda - jeden ogólny try-catch). Na przykład zespół dał mi bazę kodu z wcześniejszą bazą danych. Wiele kolumn brakowało i zostały złapane dopiero po dodaniu try catch w warstwie SQL. Po drugie, mogę zalogować nazwę metody i komunikat, w celu debugowania offline - w przeciwnym razie nie wiem, skąd pochodzi.
Chakra

Odpowiedzi:

340

Metoda powinna wychwycić wyjątek tylko wtedy, gdy może sobie z tym poradzić w rozsądny sposób.

W przeciwnym razie przekaż go w górę, mając nadzieję, że metoda znajdująca się wyżej w stosie wywołań ma sens.

Jak zauważyli inni, dobrą praktyką jest posiadanie nieobsługiwanego modułu obsługi wyjątków (z rejestrowaniem) na najwyższym poziomie stosu wywołań, aby zapewnić rejestrowanie błędów krytycznych.

Mitch Pszenica
źródło
12
Warto również zauważyć, że trybloki wiążą się z kosztami (pod względem generowanego kodu) . Dobra dyskusja w „Effective C ++” Scotta Meyersa.
Nick Meyer
28
W rzeczywistości wszystkie trybloki są wolne w każdym nowoczesnym kompilatorze C, informacja ta jest opatrzona datą Nick. Nie zgadzam się również z posiadaniem modułu obsługi wyjątków najwyższego poziomu, ponieważ tracisz informacje o lokalizacji (faktyczne miejsce, w którym instrukcja się nie powiodła).
Blindy,
31
@ Ślepo: najwyższy moduł obsługi wyjątków nie jest w stanie obsłużyć wyjątku, ale w rzeczywistości głośno krzyczy, że wystąpił nieobsługiwany wyjątek, przekazuje komunikat i kończy program w wdzięczny sposób (zwraca 1 zamiast wywołania do terminate) . To bardziej mechanizm bezpieczeństwa. Ponadto try/catchsą mniej lub bardziej bezpłatne, jeśli nie ma wyjątku. Kiedy jest jedna rozmnażanie, zużywa czas za każdym razem, gdy jest rzucana i łapana, więc łańcuch try/catchtego jedynego rzutu nie jest bezkosztowy.
Matthieu M.
17
Nie zgadzam się, że zawsze powinieneś upaść na niewyłapanym wyjątku. Nowoczesne projektowanie oprogramowania jest bardzo podzielone na przedziały, więc dlaczego należy karać resztę aplikacji (a co ważniejsze, użytkownika!) Tylko dlatego, że wystąpił jeden błąd? Awaria ostatniej rzeczy, którą chcesz zrobić, przynajmniej spróbuj dać użytkownikowi małe okno kodu, które pozwoli mu zaoszczędzić pracę, nawet jeśli nie będzie można uzyskać dostępu do reszty aplikacji.
Kendall Helmstetter Gelner
21
Kendall: Jeśli wyjątek dostanie się do modułu obsługi najwyższego poziomu, aplikacja jest z definicji w stanie niezdefiniowanym. Chociaż w niektórych szczególnych przypadkach zachowanie danych użytkownika może mieć wartość (przychodzi na myśl odzyskiwanie dokumentów programu Word), program nie powinien nadpisywać żadnych plików ani zatwierdzać bazy danych.
Hugh Brackett
136

Jak stwierdził Mitch i inni , nie powinieneś wychwytywać wyjątku, którego nie planujesz w żaden sposób obsługiwać. Podczas projektowania należy rozważyć sposób, w jaki aplikacja będzie systematycznie obsługiwać wyjątki. Zwykle prowadzi to do tego, że warstwy obsługi błędów oparte na abstrakcjach - na przykład obsługujesz wszystkie błędy związane z SQL w kodzie dostępu do danych, dzięki czemu część aplikacji, która wchodzi w interakcję z obiektami domeny, nie jest narażona na fakt, że istnieje gdzieś jest DB pod maską.

Istnieje kilka powiązanych zapachów kodu, których zdecydowanie chcesz uniknąć oprócz zapachu „łap wszystko wszędzie” .

  1. „catch, log, rethrow” : jeśli chcesz rejestrować na podstawie zakresu, napisz klasę, która emituje instrukcję logu w swoim destruktorze, gdy stos jest rozwijany z powodu wyjątku (ala std::uncaught_exception()). Wszystko, co musisz zrobić, to zadeklarować instancję rejestrowania w zakresie, który Cię interesuje, i, voila, masz rejestrację i nie ma potrzeby try/ catchlogiki.

  2. „złap, rzuć przetłumaczone” : zwykle oznacza to problem z abstrakcją. O ile nie wdrażasz rozwiązania stowarzyszonego, w którym tłumaczysz kilka konkretnych wyjątków na jeden bardziej ogólny, prawdopodobnie masz niepotrzebną warstwę abstrakcji ... i nie mów, że „może potrzebuję go jutro” .

  3. „złap, posprzątaj, wyrzuć” : to jedna z moich petów. Jeśli widzisz dużo tego, powinieneś zastosować techniki pozyskiwania zasobów to techniki inicjalizacji i umieścić część czyszczenia w destruktorze instancji obiektu janitor .

Uważam kod zaśmiecony try/ catchblokami za dobry cel do przeglądania i refaktoryzacji kodu. Wskazuje to, że obsługa wyjątków nie jest dobrze zrozumiana lub kod stał się amba i wymaga poważnego refaktoryzacji.

D.Shawley
źródło
6
# 1 jest dla mnie nowy. +1 za to. Chciałbym również zwrócić uwagę na częsty wyjątek od nr 2, który polega na tym, że jeśli często projektujesz bibliotekę, będziesz chciał przetłumaczyć wyjątki wewnętrzne na coś określonego przez interfejs biblioteki, aby zmniejszyć sprzężenie (może to mieć na myśli przez „rozwiązanie stowarzyszone”, ale nie znam tego terminu).
rmeador
3
Zasadniczo to, co powiedziałeś: parashift.com/c++-faq-lite/exceptions.html#faq-17.13
Björn
1
# 2, gdzie nie jest to zapach kodu, ale ma sens, można ulepszyć, zachowując stary wyjątek jako zagnieżdżony.
Deduplicator
1
Odnośnie # 1: std :: uncaught_exception () mówi, że istnieje nieprzechwycony wyjątek w locie, ale AFAIK tylko klauzula catch () pozwala określić, czym tak naprawdę jest wyjątek. Tak więc, chociaż możesz zarejestrować fakt, że wychodzisz z zakresu z powodu nieprzechwyconego wyjątku, tylko załączająca funkcja try / catch pozwala ci na rejestrowanie jakichkolwiek szczegółów. Poprawny?
Jeremy
@Jeremy - masz rację. Zazwyczaj loguję szczegóły wyjątku, kiedy obsługuję wyjątek. Posiadanie śladu pośrednich ramek jest bardzo przydatne. Zasadniczo musisz zalogować identyfikator wątku lub jakiś kontekst identyfikujący, aby skorelować wiersze dziennika. Użyłem Loggerklasy podobnej do log4j.Loggertej, która zawiera identyfikator wątku w każdym wierszu dziennika i wysłałem ostrzeżenie w destruktorze, gdy wyjątek był aktywny.
D.Shawley,
48

Ponieważ następne pytanie brzmi: „Złapałem wyjątek, co mam teraz zrobić?” Co zrobisz? Jeśli nic nie zrobisz - to ukrywanie błędów, a program może „po prostu nie działać” bez szansy na znalezienie tego, co się stało. Musisz zrozumieć, co dokładnie zrobisz, gdy złapiesz wyjątek i złap tylko, jeśli wiesz.

sharptooth
źródło
29

Nie musisz pokrywać każdego bloku try-catch, ponieważ try-catch może nadal wychwycić nieobsługiwane wyjątki zgłaszane w funkcjach w dalszej części stosu wywołań. Zamiast więc wypróbowywać każdą funkcję, możesz mieć ją na najwyższym poziomie logiki aplikacji. Na przykład, może istnieć procedura SaveDocument()najwyższego poziomu, która wywołuje wiele metod, które wywołują inne metody itp. Te pod-metody nie potrzebują własnych try- SaveDocument()catch , ponieważ jeśli wyrzucą, nadal będą łapane przez catch.

Jest to przydatne z trzech powodów: jest to przydatne, ponieważ masz jedno miejsce do zgłoszenia błędu: SaveDocument()blok (y) catch. Nie ma potrzeby powtarzania tego we wszystkich pod-metodach, ai tak chcesz: jedno miejsce, aby dać użytkownikowi użyteczną diagnostykę na temat czegoś, co poszło nie tak.

Po drugie, zapis jest anulowany za każdym razem, gdy zostanie zgłoszony wyjątek. Z każdym sub-metody try wzrok, jeśli jest wyjątek, można dostać się do bloku catch tej metody jest, liści wykonania funkcja, a to prowadzi przez SaveDocument(). Jeśli coś już poszło nie tak, prawdopodobnie chcesz się na tym zatrzymać.

Po trzecie, wszystkie twoje pod-metody mogą zakładać, że każde połączenie zakończy się powodzeniem . Jeśli wywołanie nie powiedzie się, wykonanie przeskoczy do bloku catch i kolejny kod nigdy nie zostanie wykonany. Może to uczynić twój kod znacznie czystszym. Oto na przykład kody błędów:

int ret = SaveFirstSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveSecondSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveThirdSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

Oto, jak można to napisać z wyjątkami:

// these throw if failed, caught in SaveDocument's catch
SaveFirstSection();
SaveSecondSection();
SaveThirdSection();

Teraz jest o wiele wyraźniej, co się dzieje.

Uwaga: bezpieczny kod wyjątku może być trudniejszy do napisania na inne sposoby: nie chcesz przeciekać pamięci, jeśli zostanie zgłoszony wyjątek. Upewnij się, że wiesz o RAII , kontenerach STL, inteligentnych wskaźnikach i innych obiektach, które zwalniają swoje zasoby w destruktorach, ponieważ obiekty są zawsze niszczone przed wyjątkami.

AshleysBrain
źródło
2
Wspaniałe przykłady. Tak, złap jak najwyżej, w jednostkach logicznych, takich jak niektóre operacje transakcyjne, takie jak ładowanie / zapisywanie / itp. Wygląd nic gorszego niż kod usiana powtarzalnych, zbędnych try- catchbloki że próba flagą każdą nieco innej permutacji jakiegoś błędu z nieco innej wiadomości, podczas gdy w rzeczywistości wszystkie one powinny zakończyć samo: transakcja lub programu awarii i wyjścia! Jeśli zdarzy się wyjątkowa awaria, zakładam, że większość użytkowników chce po prostu uratować to, co może, lub przynajmniej zostać w spokoju, bez konieczności radzenia sobie z 10 poziomami wiadomości na ten temat.
underscore_d
Chciałem tylko powiedzieć, że jest to jedno z najlepszych wyjaśnień „rzuć wcześnie, złap późno”, jakie kiedykolwiek czytałem: zwięzłe, a przykłady doskonale ilustrują twoje argumenty. Dziękuję Ci!
corderazo00
27

Herb Sutter pisał o tym problemie tutaj . Na pewno warto przeczytać.
Teaser:

„Pisanie kodu bezpiecznego dla wyjątków polega zasadniczo na pisaniu„ try ”i„ catch ”w odpowiednich miejscach.” Omawiać.

Mówiąc wprost, stwierdzenie to odzwierciedla podstawowe nieporozumienie dotyczące bezpieczeństwa wyjątkowego. Wyjątki są tylko inną formą raportowania błędów i na pewno wiemy, że pisanie kodu bezpiecznego dla błędów nie polega tylko na tym, gdzie sprawdzać kody powrotu i radzić sobie z błędami.

W rzeczywistości okazuje się, że bezpieczeństwo wyjątkowe rzadko polega na pisaniu słów „spróbuj” i „złap” - a im rzadziej, tym lepiej. Nigdy też nie zapominaj, że bezpieczeństwo wyjątków wpływa na projekt kodu; nigdy nie jest to tylko powtórka, którą można wyposażyć w kilka dodatkowych informacji o połowach, jak na przyprawę.

Tadeusz Kopec
źródło
15

Jak stwierdzono w innych odpowiedziach, powinieneś wychwycić wyjątek tylko wtedy, gdy możesz zrobić z nim jakąś sensowną obsługę błędów.

Na przykład w pytaniu, które zrodziło twoje pytanie, pytający pyta, czy bezpiecznie można zignorować wyjątki lexical_castod liczby całkowitej do ciągu. Taka obsada nigdy nie powinna zawieść. Jeśli się nie powiedzie, coś poszło nie tak w programie. Co możesz zrobić, aby odzyskać w tej sytuacji? Prawdopodobnie najlepiej jest pozwolić programowi umrzeć, ponieważ jest on w stanie, któremu nie można ufać. Więc nie obsłużenie wyjątku może być najbezpieczniejszym rozwiązaniem.

Kristopher Johnson
źródło
12

Jeśli zawsze obsłużysz wyjątki natychmiast w obiekcie wywołującym metodę, która może zgłosić wyjątek, wyjątki stają się bezużyteczne i lepiej użyć kodów błędów.

Chodzi o to, że wyjątki nie muszą być obsługiwane w każdej metodzie w łańcuchu połączeń.

starblue
źródło
9

Najlepsza rada, jaką słyszałem, to że zawsze powinieneś wychwytywać wyjątki tylko w punktach, w których możesz rozsądnie zrobić coś z wyjątkowym stanem, i że „łapanie, rejestrowanie i uwalnianie” nie jest dobrą strategią (choć czasami nieuniknioną w bibliotekach).

Donal Fellows
źródło
2
@KeithB: Uważam to za drugą najlepszą strategię. Lepiej, jeśli możesz zapisać dziennik w inny sposób.
David Thornley,
1
@KeithB: To strategia „lepsza niż nic w bibliotece”. „Złap, zaloguj się, załatw to poprawnie” lepiej tam, gdzie to możliwe. (Tak, wiem, że nie zawsze jest to możliwe.)
Donal Fellows
6

Zgadzam się z podstawowym kierunkiem twojego pytania, aby obsłużyć jak najwięcej wyjątków na najniższym poziomie.

Niektóre z istniejących odpowiedzi brzmią: „Nie musisz obsługiwać wyjątku. Ktoś inny zrobi to na stosie”. Z mojego doświadczenia jest to kiepska wymówka, aby nie myśleć o obsłudze wyjątków w aktualnie opracowanym fragmencie kodu, czyniąc wyjątek obsługą problemu innej osoby lub później.

Problem ten dramatycznie narasta w rozwoju rozproszonym, w którym może być konieczne wywołanie metody wdrożonej przez współpracownika. Następnie musisz sprawdzić zagnieżdżony łańcuch wywołań metod, aby dowiedzieć się, dlaczego on / ona rzuca na ciebie jakiś wyjątek, który można było rozwiązać znacznie łatwiej przy najgłębiej zagnieżdżonej metodzie.

Bananeweizen
źródło
5

Rada, którą dał mi kiedyś mój profesor informatyki, brzmiała: „Używaj bloków Try i Catch tylko wtedy, gdy nie można poradzić sobie z błędem przy użyciu standardowych środków”.

Jako przykład powiedział nam, że jeśli program napotka poważny problem w miejscu, w którym nie można zrobić czegoś takiego:

int f()
{
    // Do stuff

    if (condition == false)
        return -1;
    return 0;
}

int condition = f();

if (f != 0)
{
    // handle error
}

Następnie powinieneś użyć try, catch bloki. Chociaż do obsługi tego można użyć wyjątków, generalnie nie jest to zalecane, ponieważ wyjątki są kosztowne pod względem wydajności.

Mike Bailey
źródło
7
To jedna ze strategii, ale wiele osób zaleca, aby nigdy nie zwracać kodów błędów lub statusów niepowodzenia / sukcesu z funkcji, zamiast tego używając wyjątków. Obsługa błędów oparta na wyjątkach jest często łatwiejsza do odczytania niż kod oparty na kodach błędów. (Zobacz przykład AshleysBrain na to pytanie.) Zawsze też pamiętaj, że wielu profesorów informatyki ma bardzo małe doświadczenie w pisaniu prawdziwego kodu.
Kristopher Johnson
1
-1 @Sagelika Twoja odpowiedź polega na unikaniu wyjątków, więc nie musisz próbować.
Vicente Botet Escriba
3
@Kristopher: Inną dużą wadą kodu powrotu jest to, że naprawdę łatwo zapomnieć o sprawdzeniu kodu powrotu, a tuż po wywołaniu niekoniecznie jest to najlepsze miejsce do rozwiązania problemu.
David Thornley,
tak, to zależy, ale w wielu przypadkach (odkładając na bok osoby, które rzucają, kiedy tak naprawdę nie powinny), wyjątki są lepsze od kodów zwrotnych z wielu powodów. w większości przypadków pomysł, że wyjątki są szkodliwe dla wydajności, jest wielkim „[potrzebne źródło]
underscore_d
3

Jeśli chcesz przetestować wynik każdej funkcji, użyj kodów powrotu.

Celem wyjątków jest częste testowanie wyników. Chodzi o to, aby oddzielić bardziej wyjątkowe (nietypowe, rzadsze) warunki od zwykłego kodu. Dzięki temu zwykły kod jest czystszy i prostszy - ale nadal jest w stanie poradzić sobie z tymi wyjątkowymi warunkami.

W dobrze zaprojektowanym kodzie głębsze funkcje mogą rzucać, a wyższe funkcje mogą przechwytywać. Kluczem jest jednak to, że wiele funkcji „pomiędzy” będzie w ogóle wolnych od obciążeń związanych z obsługą wyjątkowych warunków. Muszą tylko być „wyjątkowo bezpieczne”, co nie oznacza, że ​​muszą złapać.

bluedog
źródło
2

Chciałbym dodać do tej dyskusji, że od C ++ 11 ma to sens, pod warunkiem, że każdy catchblok rethrowjest wyjątkiem aż do momentu, w którym można / należy go obsłużyć. W ten sposób można wygenerować ślad zwrotny . Dlatego uważam, że poprzednie opinie są częściowo nieaktualne.

Użyj std::nested_exceptionistd::throw_with_nested

Jest to opisane na StackOverflow tutaj i tutaj , jak to osiągnąć.

Ponieważ możesz to zrobić z dowolną pochodną klasą wyjątku, możesz dodać wiele informacji do takiego śladu! Możesz także spojrzeć na moją MWE na GitHub , gdzie ślad może wyglądać mniej więcej tak:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
GPMueller
źródło
2

Dano mi „możliwość” uratowania kilku projektów, a kadra kierownicza zastąpiła cały zespół programistów, ponieważ aplikacja zawierała zbyt wiele błędów, a użytkownicy byli zmęczeni problemami i rozbieganiem się. Wszystkie te podstawy kodu miały scentralizowane zarządzanie błędami na poziomie aplikacji, jak opisuje to najlepsza głosowana odpowiedź. Jeśli ta odpowiedź jest najlepszą praktyką, dlaczego nie zadziałała i nie pozwoliła poprzedniemu zespołowi programistów rozwiązać problemów? Może czasem to nie działa? Powyższe odpowiedzi nie wspominają, jak długo deweloperzy spędzają na rozwiązywaniu pojedynczych problemów. Jeśli kluczową miarą jest czas na rozwiązanie problemów, lepszym rozwiązaniem jest kodowanie przy użyciu bloków try..catch.

Jak mój zespół naprawił problemy bez znaczącej zmiany interfejsu? Proste, każda metoda została oprzyrządowana z blokadą try..catch, a wszystko było rejestrowane w punkcie błędu z nazwą metody, wartości parametrów metody połączonych w ciąg przekazywany wraz z komunikatem o błędzie, komunikacie o błędzie, nazwie aplikacji, dacie, i wersja. Dzięki tym informacjom programiści mogą uruchamiać analizy błędów, aby zidentyfikować wyjątek, który występuje najczęściej! Lub przestrzeń nazw o największej liczbie błędów. Może także sprawdzić, czy błąd występujący w module jest poprawnie obsługiwany i nie jest spowodowany wieloma przyczynami.

Inną zaletą tego rozwiązania jest to, że programiści mogą ustawić jeden punkt przerwania w metodzie rejestrowania błędów, a jednym punktem przerwania i jednym kliknięciem przycisku debugowania „step out” są w metodzie, która zakończyła się niepowodzeniem z pełnym dostępem do faktycznego obiekty w punkcie awarii, wygodnie dostępne w bezpośrednim oknie. Ułatwia to debugowanie i umożliwia przeciąganie wykonania z powrotem na początek metody w celu zduplikowania problemu w celu znalezienia dokładnej linii. Czy scentralizowana obsługa wyjątków pozwala deweloperowi zreplikować wyjątek w 30 sekund? Nie.

Stwierdzenie „Metoda powinna wychwycić wyjątek tylko wtedy, gdy może sobie z tym poradzić w rozsądny sposób”. Oznacza to, że programiści mogą przewidzieć lub napotkają każdy błąd, który może wystąpić przed wydaniem. Gdyby tak było na najwyższym poziomie, obsługa wyjątków aplikacji nie byłaby potrzebna i nie byłoby rynku elastycznego wyszukiwania i przechowywania danych.

Takie podejście pozwala również programistom znajdować i naprawiać sporadyczne problemy w produkcji! Czy chcesz debugować bez debugera w produkcji? A może wolisz odbierać połączenia i otrzymywać e-maile od zdenerwowanych użytkowników? Pozwala to naprawić problemy, zanim ktokolwiek się dowie, bez konieczności wysyłania wiadomości e-mail, wiadomości błyskawicznych lub usługi Slack ze wsparciem, ponieważ wszystko, co jest potrzebne do rozwiązania problemu, znajduje się właśnie tam. 95% problemów nigdy nie musi być powielanych.

Aby działać poprawnie, należy go połączyć ze scentralizowanym rejestrowaniem, które może przechwytywać przestrzeń nazw / moduł, nazwę klasy, metodę, dane wejściowe oraz komunikat o błędzie i przechowywać w bazie danych, aby można je było agregować w celu wyróżnienia, która metoda najbardziej zawodzi, dzięki czemu może być naprawione jako pierwsze.

Czasami programiści wybierają wyjątki w górę stosu z bloku catch, ale takie podejście jest 100 razy wolniejsze niż normalny kod, który nie rzuca. Preferowane jest przechwytywanie i zwalnianie z rejestrowaniem.

Technikę tę wykorzystano do szybkiego ustabilizowania aplikacji, która co godzinę nie działała u większości użytkowników w firmie z listy Fortune 500 opracowanej przez 12 deweloperów w ciągu 2 lat. Korzystając z tego 3000 różnych wyjątków zidentyfikowano, naprawiono, przetestowano i wdrożono w ciągu 4 miesięcy. Średnio poprawia się to co 15 minut średnio przez 4 miesiące.

Zgadzam się, że nie jest fajnie wpisywać wszystkiego, co potrzebne do instrumentowania kodu i wolę nie patrzeć na powtarzalny kod, ale dodanie 4 linii kodu do każdej metody jest tego warte na dłuższą metę.

użytkownik2502917
źródło
1
Zawijanie każdego bloku wydaje się przesadą. Szybko sprawia, że ​​kod jest rozdęty i bolesny do odczytania. Rejestrowanie śledzenia stosu z wyjątku na wyższych poziomach pokazuje, gdzie wystąpił problem, i że w połączeniu z samym błędem ogólnie wystarcza informacji, aby przejść dalej. Byłbym ciekawy, gdzie uznałeś to za niewystarczające. Tylko po to, żeby zdobyć doświadczenie kogoś innego.
user441521,
1
„Wyjątki są od 100 do 1000 razy wolniejsze niż normalny kod i nigdy nie powinny być ponownie wprowadzane” - to stwierdzenie nie jest prawdziwe w większości nowoczesnych kompilatorów i sprzętu.
Mitch Wheat
Wydaje się, że to przesada i wymaga trochę pisania, ale jest to jedyny sposób na przeprowadzenie analizy wyjątków, aby najpierw znaleźć i naprawić największe błędy, w tym sporadyczne błędy produkcyjne. Blok przechwytujący obsługuje określone błędy, jeśli są wymagane, i ma jeden wiersz kodu, który rejestruje.
user2502917,
Nie, wyjątki są bardzo powolne. Alternatywą są kody powrotne, obiekty lub zmienne. Zobacz ten komunikat o przepełnieniu stosu ... „wyjątki są co najmniej 30 000 razy wolniejsze niż kody powrotne” stackoverflow.com/questions/891217/…
user2502917
1

Oprócz powyższej porady osobiście używam try + catch + throw; z następującego powodu:

  1. Na granicy innego kodera używam try + catch + wrzuć kod napisany przeze mnie, zanim zostanie zgłoszony wyjątek dla dzwoniącego, który jest napisany przez innych, daje mi to szansę poznać pewien błąd w moim kodzie i to miejsce jest znacznie bliższe kodowi, który początkowo rzuca wyjątek, im bliżej, tym łatwiej znaleźć przyczynę.
  2. Na granicy modułów, chociaż może być napisany inny moduł, ta sama osoba.
  3. Uczenie się + cel debugowania, w tym przypadku używam catch (...) w C ++ i catch (Exception ex) w C #, dla C ++ biblioteka standardowa nie generuje zbyt wielu wyjątków, więc ten przypadek jest rzadki w C ++. Ale wspólne miejsce w C #, C # ma ogromną bibliotekę i dojrzałą hierarchię wyjątków, kod biblioteki C # rzuca mnóstwo wyjątków, teoretycznie ja (i ty) powinienem znać wszystkie wyjątki od funkcji, którą wywołałeś, i znać powód / przypadek, dlaczego te wyjątki są zgłaszane i wiedzą, jak sobie z nimi poradzić (przejść lub złapać i obsłużyć go w miejscu) z wdziękiem. Niestety w rzeczywistości bardzo trudno jest wiedzieć wszystko o potencjalnych wyjątkach przed napisaniem jednego wiersza kodu. Więc łapię wszystko i pozwalam, aby mój kod mówił na głos, logując się (w środowisku produktu) / asercji (w środowisku programistycznym), gdy naprawdę wystąpi jakikolwiek wyjątek. W ten sposób stopniowo dodam kod obsługi wyjątków. Wiem, że jest to dobre porady, ale w rzeczywistości działa dla mnie i nie znam lepszego rozwiązania tego problemu.
zhaorufei
źródło
1

Czuję się zmuszony dodać kolejną odpowiedź, chociaż odpowiedź Mike'a Wheata całkiem dobrze podsumowuje główne punkty. Myślę o tym w ten sposób. Kiedy masz metody, które robią wiele rzeczy, zwielokrotniasz złożoność, nie dodając jej.

Innymi słowy, metoda zawinięta w catch catch ma dwa możliwe wyniki. Masz wynik niebędący wyjątkiem i wynik wyjątku. Kiedy masz do czynienia z wieloma metodami, to wykładniczo wysadza w powietrze nie do zrozumienia.

Wykładniczo, ponieważ jeśli każda metoda rozgałęzia się na dwa różne sposoby, to za każdym razem, gdy wywołujesz inną metodę, poprawiasz poprzednią liczbę potencjalnych wyników. Do czasu wywołania pięciu metod masz co najmniej 256 możliwych wyników. Porównaj to z brakiem wykonywania try / catch w każdej metodzie, a masz tylko jedną ścieżkę do naśladowania.

Tak po prostu na to patrzę. Możesz mieć pokusę, aby argumentować, że każdy rodzaj rozgałęzienia robi to samo, ale try / catch są szczególnym przypadkiem, ponieważ stan aplikacji w zasadzie staje się niezdefiniowany.

Krótko mówiąc, try / catch sprawia, że ​​kod jest trudniejszy do zrozumienia.

użytkownik875234
źródło
-2

Nie musisz ukrywać każdej części kodu wewnątrz try-catch. Głównym zastosowaniem tego try-catchbloku jest obsługa błędów i występowanie błędów / wyjątków w twoim programie. Niektóre użycie try-catch-

  1. Możesz użyć tego bloku, w którym chcesz obsłużyć wyjątek, lub po prostu powiedzieć, że blok napisanego kodu może zgłosić wyjątek.
  2. Jeśli chcesz zutylizować swoje przedmioty natychmiast po ich użyciu, możesz użyć try-catchbloku.
Amit Kumawat
źródło
1
„Jeśli chcesz zutylizować swoje przedmioty natychmiast po ich użyciu, możesz użyć bloku try-catch.” Czy zamierzałeś to wesprzeć RAII / minimalny czas życia obiektu? Jeśli tak, cóż, try/ catchjest całkowicie oddzielny / ortogonalny. Jeśli chcą Państwo usunąć obiekty w mniejszym zakresie, można po prostu otworzyć nowy { Block likeThis; /* <- that object is destroyed here -> */ }- nie ma potrzeby, by zakończyć to w try/ catchchyba, że rzeczywiście trzeba catchniczego, oczywiście.
underscore_d
# 2 - Umieszczanie obiektów (które zostały ręcznie utworzone) w wyjątku wydaje mi się dziwne, może to być przydatne w niektórych językach bez wątpienia, ale ogólnie robisz to w try / wreszcie „w bloku try / oprócz”, a nie konkretnie w samym bloku wyjątków - ponieważ sam obiekt mógł być przyczyną wyjątku, a tym samym spowodować kolejny wyjątek i potencjalnie awarię.
TS