Obecnie jestem w trakcie pisania mojej pierwszej aplikacji Windows Forms. Przeczytałem teraz kilka książek o języku C #, więc mam stosunkowo dobre zrozumienie, jakie funkcje języka C # ma do czynienia z wyjątkami. Wszystkie są dość teoretyczne, ale nie mam jeszcze pojęcia, jak przełożyć podstawowe pojęcia na dobry model obsługi wyjątków w mojej aplikacji.
Czy ktoś chciałby podzielić się jakąś mądrością na ten temat? Opublikuj wszelkie typowe błędy, które popełnili nowicjusze tacy jak ja, oraz wszelkie ogólne porady dotyczące obsługi wyjątków w sposób, który zapewni stabilność i niezawodność mojej aplikacji.
Główne rzeczy, nad którymi obecnie pracuję, to:
- Kiedy należy ponownie zgłosić wyjątek?
- Czy powinienem spróbować mieć jakiś centralny mechanizm obsługi błędów?
- Czy obsługa wyjątków, które mogą być generowane, ma spadek wydajności w porównaniu z testowaniem z wyprzedzeniem, takim jak sprawdzenie, czy plik na dysku istnieje?
- Czy cały kod wykonywalny powinien być zamknięty w blokach try-catch-last?
- Czy są sytuacje, w których pusty blok catch może być akceptowalny?
Wszystkie rady otrzymane z wdzięcznością!
źródło
ApplicationException
dla tych przypadków awarii, które aplikacja powinna być w stanie rozróżnić i obsłużyć.Jest tutaj doskonały artykuł dotyczący kodu CodeProject . Oto kilka najważniejszych informacji:
źródło
Zauważ, że Windows Forms ma własny mechanizm obsługi wyjątków. Jeśli przycisk w formularzu zostanie kliknięty, a jego program obsługi zgłosi wyjątek, który nie zostanie przechwycony w programie obsługi, Windows Forms wyświetli własne okno dialogowe nieobsługiwanego wyjątku.
Aby zapobiec wyświetlaniu okna dialogowego nieobsłużonych wyjątków i przechwytywaniu takich wyjątków w celu rejestrowania i / lub udostępniania własnego okna dialogowego błędu, można dołączyć do zdarzenia Application.ThreadException przed wywołaniem Application.Run () w metodzie Main ().
źródło
Wszystkie dotychczas zamieszczone rady są dobre i warte uwagi.
Jedną z rzeczy, które chciałbym rozwinąć, jest twoje pytanie: „Czy obsługa wyjątków, które mogą być generowane, powoduje spadek wydajności w porównaniu z testami wyprzedzającymi, takimi jak sprawdzenie, czy plik na dysku istnieje?”
Naiwna zasada brzmi: „bloki try / catch są drogie”. To nie jest prawda. Próbowanie nie jest drogie. Jest to łapanie, w którym system musi utworzyć obiekt Exception i załadować go ze śladem stosu, jest to kosztowne. Istnieje wiele przypadków, w których wyjątek jest na tyle wyjątkowy, że można umieścić kod w bloku try / catch.
Na przykład, jeśli wypełniasz słownik, to:
jest często szybszy niż to:
dla każdego dodawanego elementu, ponieważ wyjątek jest zgłaszany tylko wtedy, gdy dodajesz zduplikowany klucz. (Robią to zapytania agregujące LINQ).
W podanym przykładzie użyłbym try / catch prawie bez zastanowienia. Po pierwsze, tylko dlatego, że plik istnieje, gdy go sprawdzasz, nie oznacza, że będzie istniał, gdy go otworzysz, więc i tak powinieneś naprawdę obsłużyć wyjątek.
Po drugie, i myślę, że co ważniejsze, chyba że a) twój proces otwiera tysiące plików i b) prawdopodobieństwo, że plik, który próbuje otworzyć, a nie istnieje, nie jest trywialnie niski, wydajność tworzenia wyjątku nie jest czymś, co Ty '' kiedykolwiek zauważysz. Ogólnie rzecz biorąc, gdy program próbuje otworzyć plik, próbuje otworzyć tylko jeden plik. To przypadek, w którym pisanie bezpieczniejszego kodu prawie na pewno będzie lepsze niż pisanie najszybszego możliwego kodu.
źródło
dict[key] = value
Oto kilka wskazówek, których się trzymam
Szybka awaria: jest to raczej wskazówka dotycząca generowania wyjątków. Dla każdego przyjętego założenia i każdego parametru, który dostajesz do funkcji, sprawdź, czy zaczynasz od właściwych danych i że założenia robione są poprawne. Typowe sprawdzenia obejmują, argument nie jest pusty, argument w oczekiwanym zakresie itp.
Podczas ponownego rzucania zachowaj ślad stosu - to po prostu przekłada się na użycie throw podczas ponownego rzucania zamiast rzucania new Exception (). Alternatywnie, jeśli uważasz, że możesz dodać więcej informacji, zawiń oryginalny wyjątek jako wyjątek wewnętrzny. Ale jeśli łapiesz wyjątek tylko po to, aby go zarejestrować, zdecydowanie użyj opcji throw;
Nie wychwytuj wyjątków, których nie możesz obsłużyć, więc nie przejmuj się takimi rzeczami, jak OutOfMemoryException, ponieważ jeśli wystąpią, i tak nie będziesz w stanie wiele zrobić.
Przechwyć globalne programy obsługi wyjątków i upewnij się, że rejestrujesz jak najwięcej informacji. W przypadku winforms przechwyć zarówno zdarzenia wyjątków appdomain, jak i wątków.
Wydajność należy brać pod uwagę tylko wtedy, gdy przeanalizowałeś kod i zobaczyłeś, że powoduje wąskie gardło wydajności, domyślnie optymalizuj pod kątem czytelności i projektu. Więc jeśli chodzi o twoje pierwotne pytanie dotyczące sprawdzania istnienia pliku, powiedziałbym, że to zależy.Jeśli możesz coś zrobić z brakiem pliku, to tak, zrób to, w przeciwnym razie, jeśli wszystko, co zamierzasz zrobić, to zgłosić wyjątek, jeśli plik jest nie tam, nie widzę sensu.
Zdecydowanie zdarzają się sytuacje, w których wymagane są puste bloki catch. Myślę, że ludzie, którzy twierdzą inaczej, nie pracowali nad bazami kodu, które ewoluowały w ciągu kilku wydań. Ale należy je skomentować i przejrzeć, aby upewnić się, że są naprawdę potrzebne. Najbardziej typowym przykładem są programiści używający try / catch do konwersji ciągu znaków na liczbę całkowitą zamiast używania ParseInt ().
Jeśli oczekujesz, że obiekt wywołujący Twojego kodu będzie w stanie obsłużyć warunki błędu, utwórz niestandardowe wyjątki, które szczegółowo określają, jaka jest nietypowa sytuacja i dostarczają odpowiednich informacji. W przeciwnym razie po prostu trzymaj się wbudowanych typów wyjątków tak bardzo, jak to możliwe.
źródło
Application.ThreadException
zdarzenia, gdy odwołujesz się do zdarzenia „nieobsługiwany wyjątek wątku”?Podoba mi się filozofia polegająca na tym, że nie łapię niczego, z czym nie mam zamiaru sobie poradzić, bez względu na sposób obsługi w moim konkretnym kontekście.
Nienawidzę, gdy widzę kod taki jak:
Widziałem to od czasu do czasu i dość trudno jest znaleźć problemy, gdy ktoś „zjada” wyjątki. Współpracownik, z którym miałem do czynienia, robił to i zwykle kończy się na tym, że wnosi wkład w stały strumień problemów.
Rzucam ponownie, jeśli jest coś, co moja konkretna klasa musi zrobić w odpowiedzi na wyjątek, ale problem musi zostać przekazany do metody, w której to się stało.
Uważam, że kod powinien być napisany proaktywnie, a wyjątki powinny dotyczyć wyjątkowych sytuacji, a nie unikać testowania warunków.
źródło
Właśnie wychodzę, ale krótko omówię, gdzie używać obsługi wyjątków. Po powrocie postaram się odnieść do pozostałych punktów :)
*W granicach rozsądku. Nie ma potrzeby sprawdzania, czy powiedzmy, że promień kosmiczny uderzył w twoje dane, powodując odwrócenie kilku bitów. Zrozumienie, co jest „rozsądne”, jest umiejętnością nabytą przez inżyniera. Trudno to określić ilościowo, ale łatwo się intuicyjnie. Oznacza to, że mogę łatwo wyjaśnić, dlaczego używam metody try / catch w jakimkolwiek konkretnym przypadku, ale trudno mi przyswoić inną tę samą wiedzę.
Po pierwsze, mam tendencję do odchodzenia od architektur opartych na wyjątkach. metoda try / catch nie ma trafienia związanego z wydajnością jako takiego, trafienie pojawia się, gdy zostanie zgłoszony wyjątek, a kod może być zmuszony przejść przez kilka poziomów stosu wywołań, zanim coś go obsłuży.
źródło
Złota zasada, której próbowaliśmy się trzymać, polega na obsługiwaniu wyjątku jak najbliżej źródła.
Jeśli musisz ponownie zgłosić wyjątek, spróbuj dodać do niego wyjątek, ponowne zgłoszenie wyjątku FileNotFoundException nie pomaga zbytnio, ale zgłoszenie wyjątku ConfigurationFileNotFoundException pozwoli na jego przechwycenie i wykonanie działania w dowolnym miejscu w łańcuchu.
Inną zasadą, którą staram się przestrzegać, jest to, aby nie używać try / catch jako formy przepływu programu, więc sprawdzam pliki / połączenia, upewniam się, że obiekty zostały zainicjowane, itd. Przed ich użyciem. Try / catch powinno być dla wyjątków, rzeczy, których nie możesz kontrolować.
Jeśli chodzi o pusty blok catch, jeśli robisz coś ważnego w kodzie, który wygenerował wyjątek, powinieneś co najmniej ponownie zgłosić wyjątek. Jeśli nie ma żadnych konsekwencji kodu, który spowodował, że wyjątek nie został uruchomiony, dlaczego napisałeś go w pierwszej kolejności.
źródło
możesz przechwycić zdarzenie ThreadException.
Wybierz projekt aplikacji systemu Windows w Eksploratorze rozwiązań.
Otwórz wygenerowany plik Program.cs, klikając go dwukrotnie.
Dodaj następujący wiersz kodu na początku pliku kodu:
W metodzie Main () dodaj następujący wiersz jako pierwszy wiersz metody:
Dodaj poniższy tekst poniżej metody Main ():
Dodaj kod, aby obsłużyć nieobsługiwany wyjątek w programie obsługi zdarzeń. Każdy wyjątek, który nie jest obsługiwany nigdzie indziej w aplikacji, jest obsługiwany przez powyższy kod. Najczęściej ten kod powinien rejestrować błąd i wyświetlać komunikat użytkownikowi.
odniesienie: https://blogs.msmvps.com/deborahk/global-exception-handler-winforms/
źródło
Wyjątki są drogie, ale konieczne. Nie musisz pakować wszystkiego w try catch, ale musisz upewnić się, że wyjątki zawsze zostaną ostatecznie złapane. Wiele z nich będzie zależało od twojego projektu.
Nie rzucaj ponownie, jeśli pozwolenie na powstanie wyjątku będzie równie dobre. Nigdy nie pozwól, aby błędy minęły niezauważone.
przykład:
Jeśli DoStuff pójdzie nie tak, i tak chcesz, aby został zwolniony za kaucją. Wyjątek zostanie wyrzucony do main i zobaczysz ciąg zdarzeń w śladzie stosu ex.
źródło
Wszędzie, ale metody użytkownika końcowego ... takie jak programy obsługi kliknięć przycisków
Piszę plik dziennika ... całkiem proste dla aplikacji WinForm
Nie jestem tego pewien, ale uważam, że dobrą praktyką jest rzucanie wyjątków ... Mam na myśli, że możesz zapytać, czy plik istnieje i czy nie zgłasza wyjątku FileNotFoundException
yeap
Tak, powiedzmy, że chcesz pokazać datę, ale nie masz pojęcia, w jaki sposób ta data była przechowywana (dd / mm / rrrr, mm / dd / rrrr itp.), Próbujesz ją przeanalizować, ale jeśli się nie powiedzie, po prostu kontynuuj. ... jeśli to nie ma dla ciebie znaczenia ... powiedziałbym, że tak, jest
źródło
Jedyną rzeczą, której nauczyłem się bardzo szybko, było zamknięcie absolutnie każdego fragmentu kodu, który wchodzi w interakcje z czymkolwiek poza przepływem mojego programu (tj. System plików, wywołania bazy danych, dane wejściowe użytkownika) blokami try-catch. Try-catch może spowodować spadek wydajności, ale zwykle w tych miejscach w kodzie nie będzie to zauważalne i zwróci się bezpiecznie.
Użyłem pustych bloków catch w miejscach, w których użytkownik może zrobić coś, co nie jest naprawdę „niepoprawne”, ale może zgłosić wyjątek ... przykład, który przychodzi na myśl, jest w GridView, jeśli użytkownik DoubleCLicks jest szary symbol zastępczy komórka w lewym górnym rogu uruchomi zdarzenie CellDoubleClick, ale komórka nie należy do wiersza. W takim razie nie naprawdę trzeba wysłać wiadomość, ale jeśli jej nie złapiesz, wyśle do użytkownika nieobsłużony błąd wyjątku.
źródło
Podczas ponownego zgłaszania wyjątku słowo kluczowe rzuca samodzielnie. Spowoduje to zgłoszenie przechwyconego wyjątku i nadal będzie można użyć śledzenia stosu, aby zobaczyć, skąd pochodzi.
wykonanie tej czynności spowoduje, że ślad stosu zakończy się w instrukcji catch. (unikaj tego)
źródło
Z mojego doświadczenia uznałem za stosowne wyłapać wyjątki, kiedy wiem, że zamierzam je tworzyć. W przypadku, gdy jestem w aplikacji internetowej i wykonuję Response.Redirect, wiem, że otrzymam wyjątek System.ThreadAbortException. Ponieważ jest to zamierzone, po prostu złapałem określony typ i po prostu go połknąłem.
źródło
Głęboko zgadzam się z zasadą:
Powód jest taki, że:
ForceAssert.AlwaysAssert to moja osobista droga do Trace.Assert, niezależnie od tego, czy zdefiniowano makro DEBUG / TRACE.
Być może cykl rozwoju: zauważyłem brzydkie okno dialogowe Assert lub ktoś inny narzeka mi na to, potem wracam do kodu i znajduję powód, dla którego należy zgłosić wyjątek i decyduję, jak go przetworzyć.
W ten sposób mogę napisać MÓJ kod w krótkim czasie i ochronić mnie przed nieznaną domeną, ale zawsze będąc zauważonym, jeśli zdarzy się coś nienormalnego, w ten sposób system stał się bezpieczny i bardziej bezpieczny.
Wiem, że wielu z was nie zgodzi się ze mną, ponieważ programista powinien znać każdy szczegół swojego kodu, szczerze mówiąc, w dawnych czasach jestem też purystą. Ale teraz dowiedziałem się, że powyższa polityka jest bardziej pragmatyczna.
W przypadku kodu WinForms złota zasada, której zawsze przestrzegam, to:
Dzięki temu Twój interfejs użytkownika będzie zawsze użyteczny.
W przypadku osiągnięcia wydajności spadek wydajności występuje tylko wtedy, gdy kod osiągnie catch, wykonanie kodu try bez rzeczywistego podniesionego wyjątku nie ma znaczącego wpływu.
Wyjątek powinien mieć miejsce z niewielką szansą, w przeciwnym razie nie są to wyjątki.
źródło
Musisz pomyśleć o użytkowniku. Awaria aplikacji jest ostatniąrzecz, której chce użytkownik. Dlatego każda operacja, która może się nie powieść, powinna mieć blok try catch na poziomie interfejsu użytkownika. Nie jest konieczne stosowanie try catch w każdej metodzie, ale za każdym razem, gdy użytkownik coś robi, musi być w stanie obsłużyć ogólne wyjątki. To w żaden sposób nie zwalnia Cię od sprawdzania wszystkiego, aby zapobiec wyjątkom w pierwszym przypadku, ale nie ma złożonej aplikacji bez błędów, a system operacyjny może łatwo dodać nieoczekiwane problemy, dlatego musisz przewidzieć nieoczekiwane i upewnić się, czy użytkownik chce z niego skorzystać operacja nie spowoduje utraty danych z powodu awarii aplikacji. Nie ma potrzeby, aby Twoja aplikacja uległa awarii, jeśli wychwycisz wyjątki, nigdy nie będzie ona w stanie nieokreślonym, a awaria ZAWSZE przeszkadza użytkownikowi. Nawet jeśli wyjątek znajduje się na najwyższym poziomie, brak awarii oznacza, że użytkownik może szybko odtworzyć wyjątek lub przynajmniej zapisać komunikat o błędzie, a tym samym znacznie pomóc w rozwiązaniu problemu. Z pewnością dużo więcej niż otrzymanie prostego komunikatu o błędzie, a następnie wyświetlenie tylko okna dialogowego błędu systemu Windows lub czegoś podobnego.
Dlatego NIGDY nie wolno być zarozumiałym i uważać, że aplikacja nie zawiera błędów, co nie jest gwarantowane. I bardzo małym wysiłkiem jest zawinięcie kilku bloków try catch dotyczących odpowiedniego kodu i wyświetlenie komunikatu o błędzie / zarejestrowanie błędu.
Jako użytkownik z pewnością jestem poważnie wkurzony za każdym razem, gdy zawiesza się przeglądarka, aplikacja biurowa lub cokolwiek innego. Jeśli wyjątek jest tak wysoki, że aplikacja nie może kontynuować, lepiej wyświetlić tę wiadomość i powiedzieć użytkownikowi, co ma zrobić (zrestartować, naprawić niektóre ustawienia systemu operacyjnego, zgłosić błąd itp.), Niż po prostu zawiesić się i to wszystko.
źródło