Właśnie odkryłem piękny kod w naszej aplikacji firmowej, która używa bloków Try-Catch jako operatorów logicznych.
Oznacza to: „zrób jakiś kod, jeśli spowoduje to ten błąd, zrób ten kod, ale jeśli to spowoduje ten błąd, zrób tę trzecią rzecz zamiast tego”.
Używa słowa „Wreszcie” jako wyrażenia „else”.
Wiem, że z natury jest to niewłaściwe, ale zanim wybrałem walkę, liczyłem na dobrze przemyślane argumenty.
I hej, jeśli masz argumenty przemawiające za użyciem Try-Catch w ten sposób, proszę powiedz.
Dla każdego, kto się zastanawia, językiem jest C #, a kod ma około 30+ linii i szuka konkretnych wyjątków, nie obsługuje WSZYSTKICH wyjątków.
error-handling
programming-logic
James P. Wright
źródło
źródło
try
„d. Nie każdy wyjątkowy przypadek, który ogólnie uzasadnia wyjątek, musi być śmiertelny w tym konkretnym przypadku. Czy mógłbyś to zrobić w prostszy, jednakowy lub bardziej niezawodny sposób bez wyjątków?Odpowiedzi:
Obsługa wyjątków jest zwykle drogim sposobem obsługi kontroli przepływu (z pewnością w języku C # i Javie).
Środowisko wykonawcze wykonuje dość dużo pracy, gdy konstruowany jest obiekt wyjątku - gromadzenie śladu stosu, ustalenie, gdzie obsługiwany jest wyjątek i więcej.
Wszystkie te koszty pamięci i zasobów procesora, których nie trzeba rozszerzać, jeśli do sterowania przepływem używane są instrukcje kontroli przepływu.
Ponadto istnieje problem semantyczny. Wyjątki dotyczą wyjątkowych sytuacji, a nie normalnej kontroli przepływu. Do obsługi nieprzewidzianych / wyjątkowych sytuacji należy używać obsługi wyjątków, a nie zwykłego przepływu programu, ponieważ w przeciwnym razie nieprzechwycony wyjątek powie znacznie mniej.
Oprócz tych dwóch, kwestią są inne osoby czytające kod. Używanie wyjątków w taki sposób nie jest czymś, czego spodziewa się większość programistów, więc cierpi na czytelności i zrozumiałości kodu. Kiedy widzi się „wyjątek”, myśli się - stało się coś złego, coś, co nie powinno się normalnie zdarzyć. Tak więc stosowanie wyjątków w ten sposób jest po prostu mylące.
źródło
Skąd to wiesz? Zrezygnowałem z tego rodzaju „wiedzy” i teraz po prostu wierzę, że najprostszy kod jest najlepszy. Załóżmy, że chcesz przekonwertować ciąg na opcjonalny, który jest pusty, jeśli parsowanie się nie powiedzie. Nie ma nic złego w:
Całkowicie nie zgadzam się ze zwykłą interpretacją „wyjątków dotyczą wyjątkowych warunków”. Jeśli funkcja nie może zwrócić użytecznej wartości lub metoda nie może spełnić warunków dodatkowych, należy zgłosić wyjątek. Nie ma znaczenia, jak często te wyjątki są zgłaszane, dopóki nie zostanie wykazany problem z wydajnością.
Wyjątki upraszczają kod, umożliwiając oddzielenie obsługi błędów od normalnego przepływu. Po prostu napisz najprostszy możliwy kod, a jeśli łatwiej jest użyć try-catch lub zgłosić wyjątek, zrób to.
Wyjątki upraszczają testowanie, zmniejszając liczbę ścieżek w kodzie. Funkcja bez rozgałęzień albo zakończy działanie, albo zgłosi wyjątek. Funkcja z wieloma
if
instrukcjami do sprawdzania kodów błędów ma wiele możliwych ścieżek. Bardzo łatwo jest pomylić jeden z warunków lub całkowicie go zapomnieć, aby niektóre warunki błędu zostały zignorowane.źródło
int i; if(long.TryParse(s, out i)) return i; else return null;
. TryParse zwraca false, jeśli ciąg nie mógł zostać przeanalizowany. Jeśli można go przeanalizować, ustawia argument wyjściowy na wyniki analizy i zwraca true. Nie występują wyjątki (nawet wewnętrznie w TryParse), a zwłaszcza żadnych wyjątków typu oznaczającego błąd programisty, jakFormatException
zawsze .NET .Debugowanie i prace konserwacyjne są bardzo trudne, gdy przepływ sterowania jest wykonywany z wykorzystaniem wyjątków.
Z natury wyjątki zaprojektowano jako mechanizm zmiany normalnego przepływu sterowania programem - wykonywania nietypowych czynności, powodujących nietypowe skutki uboczne, jako sposób na wyjście ze szczególnie ciasnego powiązania, którego nie da się poradzić z mniej skomplikowanymi znaczy. Wyjątki są wyjątkowe. Oznacza to, że w zależności od konkretnego środowiska, w którym pracujesz, użycie wyjątku dla regularnego przepływu kontroli może spowodować:
Niewydolność Dodatkowe obręcze, przez które środowisko musi przejść, aby bezpiecznie wykonać stosunkowo trudne zmiany kontekstu wymagane dla wyjątków, wymagają oprzyrządowania i zasobów.
Trudności debugowania Czasami przydatne (podczas próby debugowania programu) informacje są wyrzucane przez okno, gdy występują wyjątki. Możesz stracić kontrolę nad stanem programu lub historią, które są istotne dla zrozumienia zachowania w czasie wykonywania
Problemy konserwacyjne Przebieg wykonania jest trudny do przejścia przez skoki wyjątków. Poza tym wyjątek może zostać wygenerowany z kodu typu czarnej skrzynki, który może nie zachowywać się w sposób łatwy do zrozumienia podczas zgłaszania wyjątku.
Słabe decyzje projektowe Programy zbudowane w ten sposób zachęcają do myślenia, które w większości przypadków nie jest łatwe do eleganckiego rozwiązania problemów. Złożoność końcowego programu zniechęca programistę do pełnego zrozumienia jego wykonania i zachęca do podejmowania decyzji, które prowadzą do krótkoterminowych ulepszeń przy wysokich kosztach długoterminowych.
źródło
Widziałem ten wzór używany kilka razy.
Istnieją 2 główne problemy:
goto
. Skoki są uważane za szkodliwe z wielu powodów. Powinny być stosowane, jeśli wszystkie alternatywy mają znaczące wady. W rzeczywistości w całym moim kodzie pamiętam tylko 2 przypadki, w których skoki były zdecydowanie najlepszym rozwiązaniem.źródło
Czasami wyjątki są najszybsze. Widziałem przypadki, w których wyjątki zerowych obiektów były szybsze nawet w Javie niż użycie struktur kontrolnych (w tej chwili nie mogę cytować badania, więc musisz mi zaufać). Problem pojawia się, gdy Java musi poświęcić trochę czasu i zapełnić ślad stosu niestandardowej klasy wyjątku zamiast używać rodzimych (które wydają się przynajmniej częściowo buforowane). Zanim powiesz, że coś jest jednostronnie szybsze lub wolniejsze, dobrze byłoby przeprowadzić test porównawczy.
W Pythonie jest nie tylko szybsze, ale bardziej poprawne jest zrobienie czegoś, co może spowodować wyjątek, a następnie obsłużenie błędu. Tak, możesz wymusić system typów, ale jest to sprzeczne z filozofią języka - zamiast tego powinieneś po prostu wywołać metodę i złapać wynik! (Testowanie, czy plik jest zapisywalny, jest podobne - po prostu spróbuj do niego zapisać i wychwyć błąd).
Widziałem czasy, kiedy szybsze jest robienie czegoś głupiego, jak tabele zapytań, których nie było, niż stwierdzenie, czy tabela istnieje w PHP + MySQL ( pytanie, w którym porównuję, jest to właściwie moja jedyna zaakceptowana odpowiedź z negatywnymi głosami) .
Wszystko to mówi, że stosowanie wyjątków powinno być ograniczone z kilku powodów:
try...catch
zwolennicy kontroli przepływu ogólnie (z mojego doświadczenia) nie postępują zgodnie z filozofią „minimalizuj kod w bloku prób”.else if
blok. Wystarczy powiedzieć (a jeśli ktoś skontaktuje się z „Ale mógłby użyć różnych klas wyjątków”, moja odpowiedź brzmi: „Idź do swojego pokoju i nie wychodź, dopóki nie pomyślisz o tym, co powiedziałeś”).Exception
, dla reszty świata (ponieważ nie jest zwolennikiemtry...catch
filozofii kontroli przepływu), oznacza, że coś weszło w niestabilny (choć być może możliwy do odzyskania) stan. Niestabilne stany są ZŁE i powinno nas wszystkich utrzymywać w nocy, jeśli rzeczywiście mamy wyjątki, których można uniknąć (tak naprawdę to mnie przeraża, żadnych kłamstw).try...catch
kontrola przepływu jest sprzeczna z powszechnie uznawanymi najlepszymi praktykami. Oznacza to, że nauka nowego projektu zajmuje więcej czasu, co oznacza wzrost liczby osobogodzin bez absolutnie żadnego zysku.try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();}
. W bardziej tradycyjnymif...else
scenariuszucloseSomething()
jest mniej prawdopodobne (ponownie, osobiste doświadczenie), że będzie to zadanie kopiowania i wklejania. (Trzeba przyznać, że ten konkretny argument ma więcej wspólnego z ludźmi, których spotkałem, niż z samą filozofią).źródło
Moim głównym argumentem jest to, że użycie try / catch dla logiki przerywa logiczny przepływ. Podążanie za „logiką” poprzez konstrukcje nielogiczne jest (dla mnie) sprzeczne z intuicją i mylące. Przyzwyczaiłem się czytać moją logikę jako „jeśli Warunek to A jeszcze B”. Czytanie tego samego zdania, co „Spróbuj złapać, a następnie wykonać B” wydaje się dziwne. Byłoby jeszcze dziwniejsze, gdyby instrukcja
A
była prostym przypisaniem, wymagającym dodatkowego kodu, aby wymusić wyjątek, jeślicondition
jest fałszywy (a zrobienie tego prawdopodobnie wymagałobyif
-statement i tak).źródło
Cóż, to kwestia etykiety, zanim „zaczniesz z nimi kłócić się”, jak sam twierdzisz, uprzejmie zapytałbym: „Dlaczego używają obsługi wyjątków w tych różnych miejscach?”.
Mam na myśli kilka możliwości:
... Myślę, że wszystkie z nich są jednakowo prawdopodobne. Zapytaj ich więc ładnie.
źródło
Try Catch powinien być używany tylko do obsługi wyjątków. Co więcej, specjalna obsługa wyjątków. Twój try catch powinien wychwycić tylko oczekiwane wyjątki, w przeciwnym razie nie jest dobrze uformowany. Jeśli potrzebujesz użyć catch all try catch, prawdopodobnie robisz coś złego.
EDYTOWAĆ:
Powód: Możesz argumentować, że jeśli użyjesz try catch jako operatora warunkowego, jak zamierzasz uwzględnić PRAWDZIWE wyjątki?
źródło
catch
klauzuli innej niż ta, która łapie „nierealne” wyjątki?Wyjątek dotyczy sytuacji wyjątkowej. Czy program działa zgodnie ze zwykłym przepływem pracy?
źródło
Powód zastosowania wyjątków:
Powody, dla których nie należy stosować wyjątków:
Na końcu:
Celem jest napisanie kodu informującego o tym, co się dzieje. Wyjątki mogą pomóc / utrudnić to w zależności od tego, co robi kod. Próba złapania w Pythonie dla KeyError w odwołaniu do słownika jest doskonale (o ile znasz język) próba / catch dla tej samej KeyError oddalonej o pięć warstw funkcji jest niebezpieczna.
źródło
W niektórych sytuacjach używam try-> catch jako kontroli przepływu. Jeśli twoja podstawowa logika zależy od czegoś, co się nie powiedzie, ale nie chcesz po prostu rzucić wyjątek i wyjść ... Po to jest blok try-> catch. Piszę wiele niemonitorowanych skryptów unixowych po stronie serwera, a ich o wiele ważniejsze jest, aby nie zawiodły, niż by ładnie zawiodły.
Więc spróbuj Plan A, a jeśli umrze, planowanie połowów i biegać z Planu B ... A jeśli nie Plan B, wykorzystanie wreszcie skopać Plan C, który będzie albo jeden fix z nieudanych usług w A lub B, lub na stronie mnie.
źródło
Zależy to od języka i być może od zastosowanej implementacji. Standardem w C ++ jest zapisywanie wyjątków dla naprawdę wyjątkowych wydarzeń. Z drugiej strony, w Pythonie jedną ze wskazówek jest „ łatwiej prosić o wybaczenie niż o pozwolenie ”, dlatego zachęca się do integracji bloków try / oprócz w logice programu.
źródło
To zły nawyk, ale robię to od czasu do czasu w Pythonie. Zamiast sprawdzać, czy plik istnieje, po prostu próbuję go usunąć. Jeśli rzuca wyjątek, łapię i po prostu przechodzę dalej wiedząc, że go nie ma. <== (niekoniecznie prawda, jeśli inny użytkownik jest właścicielem pliku, ale robię to w katalogu osobistym użytkownika, więc NIE POWINIEN się zdarzyć).
Nadużywane to zła praktyka.
źródło
Podoba mi się sposób, w jaki Apple to definiuje : wyjątki dotyczą tylko błędów programisty i krytycznych błędów środowiska wykonawczego. W przeciwnym razie użyj kodów błędów. Pomaga mi zdecydować, kiedy go użyć, myśląc sobie: „Czy to błąd programisty?”
źródło
To brzmi jak użycie obsługi wyjątków do zbudowania logiki goto na języku, który nie ma polecenia goto.
Z wielu powodów, dla których goto logika jest zła, możesz odnieść się do tego pytania: https://stackoverflow.com/questions/46586/goto-still-consanted-harmful
źródło