Wydaje się, że istnieje pewna zgoda co do tego, że komunikaty o wyjątkach powinny zawierać przydatne szczegóły .
Dlaczego wiele typowych wyjątków od składników systemu nie zawiera użytecznych szczegółów?
Kilka przykładów:
- NET
List
dostęp wskaźnikArgumentOutOfRangeException
nie nie mów mi, że wartość indeksu został osądzony i był nieprawidłowy, ani nie mów mi dozwolony zakres. - Zasadniczo wszystkie komunikaty wyjątków ze standardowej biblioteki MSVC C ++ są całkowicie bezużyteczne (w takim samym brzmieniu jak powyżej).
- Wyjątki Oracle w .NET, mówiąc (sparafrazowane) „TABELA LUB WIDOK nie znaleziono”, ale nie który .
Wydaje mi się więc, że komunikaty wyjątków w przeważającej części nie zawierają wystarczających szczegółów, aby były przydatne. Czy moje oczekiwania nie są zgodne? Czy źle stosuję wyjątki, że to zauważam? A może moje wrażenie jest błędne: większość wyjątków czy rzeczywiście dostarczyć użytecznych szczegółów?
c#
c++
exceptions
Martin Ba
źródło
źródło
Odpowiedzi:
Wyjątki nie zawierają użytecznych szczegółów, ponieważ koncepcja wyjątków nie jest jeszcze wystarczająco dojrzała w dyscyplinie inżynierii oprogramowania, więc wielu programistów nie rozumie ich w pełni, a zatem nie traktuje ich odpowiednio.
Tak,
IndexOutOfRangeException
powinien zawierać dokładny indeks, który był poza zakresem, a także zakres, który był ważny w momencie jego wyrzucenia, i jest to godne pogardy dla twórców środowiska uruchomieniowego .NET, że tak nie jest. Tak,table or view not found
wyjątek Oracle powinien zawierać nazwę tabeli lub widoku, która nie została znaleziona, i ponownie fakt, że nie jest to godne pogardy dla każdego, kto jest za to odpowiedzialny.W dużej mierze zamieszanie wynika z błędnego pierwotnego pomysłu, że wyjątki powinny zawierać wiadomości czytelne dla człowieka, co z kolei wynika z niezrozumienia, o co chodzi w wyjątkach, więc jest to błędne koło.
Ponieważ ludzie uważają, że wyjątek powinien zawierać komunikat czytelny dla człowieka, uważają, że wszelkie informacje przenoszone przez wyjątek powinny być również sformatowane w komunikacie czytelnym dla człowieka, a następnie albo znudzeni są pisaniem całej wiadomości czytelnej dla człowieka budowanie kodu lub obawiają się, że może to ujawnić niewskazaną ilość informacji wszystkim wścibskim oczom, którzy mogą zobaczyć wiadomość. (Problemy bezpieczeństwa wymienione w innych odpowiedziach).
Ale prawda jest taka, że nie powinni się tym martwić, ponieważ wyjątek nie powinien zawierać przesłania czytelnego dla człowieka. Wyjątki to rzeczy, które tylko programiści powinni widzieć i / lub sobie z nimi radzić. Jeśli kiedykolwiek istnieje potrzeba przedstawienia użytkownikowi informacji o awarii, należy to zrobić na bardzo wysokim poziomie, w wyrafinowany sposób oraz w języku użytkownika, który, statystycznie rzecz biorąc, prawdopodobnie nie jest angielskim.
Zatem dla nas, programistów, „komunikat” wyjątku jest nazwą klasy wyjątku i wszelkie inne informacje dotyczące wyjątku powinny zostać skopiowane do (końcowych / tylko do odczytu) zmiennych składowych obiektu wyjątku. Najlepiej, jeśli jest to możliwe. W ten sposób żadna wiadomość nie musi (lub powinna) zostać wygenerowana i dlatego żadne wścibskie oczy nie mogą jej zobaczyć.
Aby rozwiązać problem wyrażony przez Thomasa Owensa w komentarzu poniżej:
Tak, oczywiście, na pewnym poziomie, to będzie tworzyć wiadomości dziennika dotyczące wyjątku. Ale już widzisz problem z tym, co mówisz: z jednej strony komunikat dziennika wyjątków bez śladu stosu jest bezużyteczny, ale z drugiej strony nie chcesz, aby użytkownik widział cały ślad stosu wyjątków. Ponownie naszym problemem jest to, że nasza perspektywa jest wypaczona przez tradycyjne praktyki. Pliki dziennika były tradycyjnie zwykłym tekstem, co mogło być w porządku, gdy nasza dyscyplina była w powijakach, ale być może już nie: jeśli istnieje obawa o bezpieczeństwo, plik dziennika musi być binarny i / lub szyfrowany.
Niezależnie od tego, czy jest to tekst binarny, czy zwykły, plik dziennika należy traktować jako strumień, w którym aplikacja serializuje informacje debugowania. Taki strumień byłby tylko dla programistów, a zadanie generowania informacji debugowania dla wyjątku powinno być tak proste, jak serializacja wyjątku do strumienia dziennika debugowania. W ten sposób, patrząc na dziennik, zobaczysz nazwę klasy wyjątku (która, jak już wspomniałem, jest dla wszystkich praktycznych celów „komunikatem”), każdą zmienną składową wyjątku, która opisuje wszystko, co jest istotne - oraz -praktyczne do włączenia do dziennika i śledzenia całego stosu. Zwróć uwagę, że w tym procesie wyraźnie brakuje formatowania czytelnego dla człowieka komunikatu o wyjątku .
PS
Jeszcze kilka moich przemyśleń na ten temat można znaleźć w odpowiedzi: Jak napisać dobry komunikat o wyjątku
PPS
Wygląda na to, że wiele osób zostało odreagowanych przez moją sugestię dotyczącą binarnych plików dziennika, więc ponownie poprawiłem odpowiedź, aby jeszcze bardziej wyjaśnić, że to, co sugeruję tutaj, nie polega na tym, że plik dziennika powinien być binarny, ale że plik dziennika może być binarny, jeśli to konieczne.
źródło
Z mojego doświadczenia wynika, że istnieje wiele powodów, dla których wyjątki nie zawierają użytecznych informacji. Oczekuję, że tego rodzaju powody będą dotyczyć również komponentów systemu - ale nie jestem tego pewien.
Trochę? To znaczy, wyjątki powinny mieć fajne wiadomości, ale są też wyjątkami . Powinieneś spędzać czas na projektowaniu kodu, aby uniknąć wyjątkowych warunków, lub na pisaniu kodu, aby poradzić sobie z wyjątkowymi warunkami (ignorowanie wiadomości), nie wykorzystując ich jako rodzaju interaktywnego mechanizmu sprzężenia zwrotnego podczas kodowania. Nie można ich używać do celów debugowania, ale w większości sytuacji należy to ograniczyć do minimum. To, że zauważyłeś ten problem, niepokoi mnie, że nie robisz wystarczająco dobrej roboty, aby im zapobiegać.
źródło
Nie mam nadmiaru doświadczenia w C #, a konkretnie w C ++, ale mogę ci to powiedzieć - wyjątki napisane przez programistów 9 na 10 razy są bardziej przydatne niż jakikolwiek ogólny wyjątek, jaki kiedykolwiek znajdziesz, kropka.
Idealnie tak, ogólny wyjątek wskaże ci dokładnie, dlaczego wystąpił błąd i będziesz mógł go naprawić z łatwością - ale realistycznie, w dużych aplikacjach z wieloma klasami, które mogą generować wiele różnych rodzajów wyjątków lub z takimi samymi wyjątkami, zawsze bardziej wartościowe jest pisanie własnych danych wyjściowych do powrotu po błędzie, niż poleganie na domyślnym komunikacie.
Tak powinno być, ponieważ jak zauważyło wiele osób, niektóre aplikacje nie chcą wyświetlać komunikatu o błędzie, którego nie chcą widzieć użytkownicy ze względów bezpieczeństwa lub aby ich nie pomylić.
Zamiast tego powinieneś przewidzieć w swoim projekcie, jakie rodzaje błędów mogą być zgłaszane w Twojej aplikacji (i zawsze będą występować błędy) i pisać komunikaty wychwytujące błędy, które pomogą Ci zidentyfikować problem.
Nie zawsze ci to pomoże - ponieważ nie zawsze możesz przewidzieć, jaki komunikat o błędzie będzie przydatny - ale jest to pierwszy krok do lepszego zrozumienia własnej aplikacji w dłuższej perspektywie.
źródło
Pytanie dotyczy w szczególności tego, dlaczego tak wiele wyjątków zgłaszanych przez „komponenty systemu” (znane również jako standardowe klasy bibliotek) nie zawiera użytecznych szczegółów.
Niestety większość programistów nie pisze podstawowych komponentów w standardowych bibliotekach, a szczegółowe dokumenty projektowe lub inne uzasadnienia projektowe niekoniecznie są upubliczniane. Innymi słowy, możemy nigdy nie mieć pewności.
Należy jednak pamiętać o dwóch kluczowych kwestiach, dlaczego szczegółowe informacje o wyjątkach mogą nie być pożądane lub ważne:
Można użyć wyjątku, wywołując kod w dowolny sposób: standardowa biblioteka nie może nakładać ograniczeń na sposób wykorzystania wyjątku. W szczególności może być wyświetlany użytkownikowi. Rozważ indeks tablicy poza granicami: może to dać użyteczne informacje atakującemu. Projektant języka nie ma pojęcia, w jaki sposób aplikacja wykorzysta zgłoszony wyjątek, a nawet jaki to rodzaj aplikacji (np. Aplikacja internetowa lub komputer stacjonarny), więc pominięcie informacji może być bezpieczniejsze z punktu widzenia bezpieczeństwa.
Wyjątki nie powinny być wyświetlane użytkownikowi. Zamiast tego wyświetl przyjazny komunikat o błędzie i zaloguj wyjątek do lokalizacji, do której atakujący nie ma dostępu (jeśli dotyczy). Po zidentyfikowaniu błędu programista powinien debugować kod, sprawdzając ramki stosu i ścieżki logiczne. W tym momencie programista ma więcej informacji, niż wyjątek mógłby kiedykolwiek mieć nadzieję.
źródło
Po pierwsze, pozwól mi rozbić bańkę, mówiąc, że nawet jeśli wiadomość diag jest załadowana informacjami, które prowadzą do dokładnej linii kodu i polecenia podrzędnego w ciągu 4 sekund, istnieje prawdopodobieństwo, że użytkownicy nigdy go nie zapisają ani nie przekażą pracownikom pomocy technicznej i powiedzą ci: „Cóż, to mówiło coś o naruszeniu ... Nie wiem, czy to wyglądało na skomplikowane!”
Piszę oprogramowanie i wspieram wyniki innych programów od ponad 30 lat i osobiście obecna jakość komunikatu o wyjątku nie ma praktycznie nic wspólnego z bezpieczeństwem, niezależnie od tego, jak wyniki końcowe pasowały do ich modelu wszechświat, o wiele więcej wspólnego z faktem, że tak wielu w naszej branży było pierwotnie samoukami i po prostu nigdy nie zawierało lekcji komunikacji. Może gdybyśmy WYMUSIŁY wszystkich nowych programistów na kilka lat do pozycji serwisowej, gdzie musieliby się dowiedzieć, co poszło źle, zrozumieliby znaczenie przynajmniej pewnej formy precyzji.
Niedawno w trakcie przebudowy aplikacji podjęto decyzję, że kody zwrotne będą należeć do jednej z trzech grup:
(100 zostało z jakiegoś powodu pominięte)
W każdym konkretnym przepływie pracy nasza myśl została ponownie wykorzystana lub kod ogólny użyłby ogólnych zwrotów (> 199) dla błędów krytycznych, pozostawiając nam 100 błędów krytycznych możliwych dla przepływu pracy. Przy lekkim rozróżnieniu danych w komunikacie błędy takie jak nie znaleziono pliku mogą używać tego samego kodu i różnicować się z komunikatami.
Gdy kod wrócił od kontrahentów, nie uwierzyłbyś w nasze zdziwienie, gdy praktycznie KAŻDY JEDEN FATALNY BŁĄD był kodem zwrotnym 101.
Uważam, że odpowiedzią na twoje pytanie jest to, że wiadomości są tak bezsensowne, ponieważ po ich utworzeniu miały być symbolami zastępczymi, do których nikt nie wrócił. W końcu ludzie wymyślili, jak rozwiązać problemy nie z powodu wiadomości, ale DISPITE je.
Od tego czasu samoukowie nigdy nie mieli dobrego przykładu, co powinien zawierać wyjątek. Dodaj do tego, że więcej użytkowników nie czyta wiadomości, a tym bardziej spróbuj przekazać je do obsługi (widziałem komunikaty o błędach, które zostały wycięte i wklejone przez używane, które zostały następnie zredagowane przed wysłaniem z późniejszym komentarzem, że to wydawało mi się, że to dużo informacji i prawdopodobnie nie mogłem tego wszystkiego chcieć, więc losowo usunęli kilka z nich.
I spójrzmy prawdzie w oczy, ze zdecydowanie za dużo (nie wszystkie, ale za dużo) nowej generacji koderów, jeśli jest to więcej pracy i nie dodaje flasha, po prostu nie jest tego warte ...
Ostatnia uwaga: jeśli komunikat o błędzie zawiera kod błędu / powrotu, wydaje mi się, że gdzieś w wykonanych modułach powinna znajdować się linia czytająca coś w rodzaju „jeśli warunek zwraca wartość-kod” i warunek powinien powiedzieć, dlaczego kod powrotu wystąpił. Wydaje się to prostym logicznym podejściem, ale na całe życie spróbuj po prostu przekonać Microsoft, co się stało, gdy aktualizacja systemu Windows nie powiodła się na CODE 80241013 lub innym bardzo unikalnym identyfikatorze. Trochę smutne, prawda?
źródło
chances are the users will never write it down... and you will be told "Well it said something about a violation..."
Dlatego używasz narzędzia do rejestrowania wyjątków, aby automatycznie wygenerować raport o błędach zawierający ślad stosu, a nawet wysłać go na serwer. Raz miałem jednego użytkownika, który nie był zbyt techniczny. Za każdym razem, gdy przesyłała komunikat z rejestratora błędów, pojawiało się coś takiego: „Nie jestem pewien, co zrobiłem źle, ale ...” bez względu na to, ile razy wyjaśniłem, że ten błąd oznaczał błąd po mojej stronie . Ale zawsze otrzymywałem od niej raporty o błędach!Chociaż zgadzam się, że wyjątki powinny zawierać jak najwięcej informacji, a przynajmniej być mniej ogólne. W przypadku nie znalezienia tabeli, nazwa tabeli byłaby miła.
Ale wiesz więcej o tym, co próbowałeś zrobić w miejscu w kodzie, w którym otrzymałeś wyjątek. Chociaż często naprawdę nie możesz wiele zrobić, aby naprawić sytuację, gdy coś pójdzie nie tak w bibliotece, na którą nie masz wpływu, możesz dodać o wiele bardziej przydatne informacje, w której sytuacji coś poszło nie tak.
W przypadku braku tabeli nie pomoże ci to, jeśli powiesz, że tabela, której nie można znaleźć, nazywa się STUDENCI, ponieważ nie masz takiej tabeli, a łańcucha nie ma w twoim kodzie.
Ale jeśli złapiesz wyjątek i wrzucisz go ponownie za pomocą instrukcji SQL, którą próbujesz wykonać, będzie ci lepiej, ponieważ okazuje się, że próbowałeś wstawić rekord o polu nazwy Robert ”); UCZNIOWIE STOŁU; (Zawsze jest xkcd!)
Aby więc walczyć z mniej niż informacyjnymi wyjątkami: spróbuj złapać ponownie, podając więcej informacji specyficznych dla tego, co próbujesz zrobić.
Powinienem chyba dodać, że jest to raczej odpowiedź na pytanie, dlaczego w pytaniu, że prawdopodobnym powodem, dla którego twórcy bibliotek nie skupili się na ulepszaniu komunikatów wyjątków, jest to, że nie wiedzą dlaczego próbowano czegoś, co nie powiodło się, logika ta znajduje się w kodzie wywołującym.
źródło
throw_with_nested
dużo używać .Aby dać nieco inną odpowiedź: prawdopodobnie został naruszony kod, aby sprecyzować:
Dodaj presję, aby dostarczać dokładnie do specyfikacji (w obawie przed odrzuceniem podczas przeglądu / testowania) w jak najkrótszym czasie i przy minimalnym zamieszaniu, wtedy masz przepis na całkowicie zgodny i niepomocny wyjątek w bibliotece.
źródło
Wyjątki wiążą się z kosztami specyficznymi dla języka i wdrożenia.
Na przykład wyjątki w C ++ są wymagane do zniszczenia wszystkich żywych danych między wyrzucaniem ramki wywołania a przechwytywaniem ramki wywołania, a to jest kosztowne. Dlatego programiści nie chcą zbyt często używać wyjątków.
W Ocaml zgłaszanie wyjątków jest prawie tak szybkie jak C
setjmp
(jego koszt nie zależy od liczby przemieszczanych ramek wywołań), więc programiści mogą z niego korzystać bardzo często (nawet w nietypowych, bardzo częstych przypadkach). W przeciwieństwie do tego wyjątki w C ++ są wystarczająco ciężkie, więc prawdopodobnie nie będziesz ich używać tak, jak w Ocaml.Typowym przykładem jest wyszukiwanie rekursywne lub eksploracja, które można „zatrzymać” wewnątrz dość głębokiej rekurencji (np. Znaleźć liść na drzewie lub funkcję unifikacyjną). W niektórych językach propagowanie tego warunku wśród każdego dzwoniącego jest szybsze (a więc bardziej idiomatyczne). W innych językach zgłoszenie wyjątku jest szybsze.
Tak więc w zależności od języka (i nawyków programistów używających go) wyjątek może zawierać wiele użytecznych szczegółów, lub wręcz przeciwnie może być użyty jako szybki skok nielokalny i zawierać tylko bardzo przydatne dane.
źródło
Co sprawia, że uważasz, że wartość indeksu lub wymagany zakres lub nazwa tabeli jest użytecznym szczegółem, na przykład w wyjątku?
Wyjątki nie są mechanizmem obsługi błędów; są mechanizmem odzyskiwania .
Wyjątkiem jest bąbelkowanie do poziomu kodu, który może obsłużyć wyjątek. Gdziekolwiek to jest poziom, albo ty masz informacje potrzebne, czy to nie jest istotne. Jeśli potrzebujesz informacji, ale nie masz do nich bezpośredniego dostępu, wyjątek nie jest obsługiwany na odpowiednim poziomie.
Pewnego razu mogę wymyślić, gdzie dodatkowe informacje mogą być pomocne, na absolutnym najwyższym poziomie aplikacji, w której następuje awaria i zrzut zrzutu błędu; ale wyjątek stanowi dostęp do tych informacji, ich kompilacja i przechowywanie.
Nie twierdzę, że można po prostu wszędzie „rzucić nowy wyjątek” i nazwać to dobrym, można napisać złe wyjątki. Ale dołączanie nieistotnych informacji nie jest konieczne do ich prawidłowego wykorzystania.
źródło