W jaki sposób używanie try catch do obsługi wyjątków jest najlepszą praktyką


201

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 catchblok

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ę.


128
Fragment nr 1 jest w 99,999% przypadków niedopuszczalny.
leppie

22
Wyświetlanie wyjątku bezpośrednio użytkownikowi nigdy nie jest dobrym pomysłem głównie z dwóch powodów: 1. jeśli jest to zwykły użytkownik (użytkownicy), będzie zirytowany czytaniem komunikatu o błędzie, który mówi mu bardzo niewiele. 2. jeśli jest tak zwanym hakerem, może uzyskać przydatne informacje. Najlepszą praktyką IMO jest rejestrowanie wyjątków i wyświetlanie przyjaznego komunikatu o błędzie.
Leri

4
@leppie Jeśli pojawi się coś nieoczekiwanego (jak NullReferencelub ArgumentNullnie 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.
Leri

14
Użycie bloku try-catch w celu ukrycia wyjątku jest zazwyczaj wynikiem leniwego programowania. Jest to skrót, który jest często używany zamiast pisać kod weryfikacyjny do testowania danych wejściowych. Bardzo czasami zdarzają się wyjątki, które nie wpływają na działanie twojego kodu i ukrywanie go w ten sposób może być w porządku. Jest to jednak dość rzadkie.
Corey,

12
@Toan, cóż, jeśli jest to zadanie wsadowe, łapię na najwyższym poziomie (głównym), aby się zalogować, a następnie ponownie rzucam, aby uruchomić alarm, że zadanie zakończyło się nienormalnie. Jeśli jest to aplikacja internetowa, pozwalam wyjątkowi przesłać bąbelek do globalnego modułu obsługi, loguję się, a następnie przekierowuję użytkownika na ekran błędu. Scenariusz przypadku użycia określa, co robisz z tym wyjątkiem po zalogowaniu się lub innej obsłudze.
Anthony Pegram,

Odpowiedzi:


300

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:

    • W przypadku aplikacji interfejsu użytkownika: aby wysłać ją użytkownikowi z komunikatem z przeprosinami (winforms)
    • W przypadku aplikacji usługi lub konsoli: zaloguj się do pliku (usługi lub konsoli)

Następnie zawsze dołączam każdy fragment kodu uruchamiany zewnętrznie w try/catch:

  • Wszystkie zdarzenia uruchamiane przez infrastrukturę Winforms (Load, Click, SelectedChanged ...)
  • Wszystkie zdarzenia uruchamiane przez komponenty stron trzecich

Następnie zamykam w „try / catch”

  • Wszystkie operacje, które znam, mogą nie działać przez cały czas (operacje IO, obliczenia z potencjalnym podziałem zerowym ...). W takim przypadku rzucam nowy, ApplicationException("custom message", innerException)aby śledzić, co naprawdę się wydarzyło

Ponadto staram się jak najlepiej sortować wyjątki . Istnieją wyjątki, które:

  • należy natychmiast pokazać użytkownikowi
  • wymagają dodatkowego przetwarzania, aby poskładać rzeczy, gdy się zdarzy, aby uniknąć problemów z kaskadowaniem (np .: wstaw .EndUpdate w finallysekcji podczas TreeViewwypełniania)
  • użytkownik nie dba o to, ale ważne jest, aby wiedzieć, co się stało. Więc zawsze je loguję:

    • W dzienniku zdarzeń
    • lub w pliku .log na dysku

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:

  • Pamiętaj, że WSZYSTKIE wyjątki są propagowane do najwyższego poziomu . Nie jest konieczne umieszczanie modułów obsługi wyjątków wszędzie.
  • Funkcje wielokrotnego użytku lub głęboko wywoływane nie muszą wyświetlać ani rejestrować wyjątków: są one automatycznie dodawane do pęcherzyków lub ponownie wyrzucane z niektórymi niestandardowymi komunikatami w moich programach obsługi wyjątków.

Wreszcie:

Zły:

// DON'T DO THIS, ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

Bezużyteczny:

// DONT'T DO THIS, ITS USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

Próba wreszcie bez połowu jest całkowicie ważna:

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT
    listView1.EndUpdate();
}

Co robię na najwyższym poziomie:

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --

    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

Co robię w niektórych nazwanych funkcjach:

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

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.

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}

98
catch(Exception ex) { throw ex; }w C # jest gorszy niż redundantny (niezależnie od rodzaju wyjątku, który wychwytujesz). Aby ponownie rzucić, użyj throw;. W pierwszym przypadku wyjątek będzie wyglądał, jakby pochodził z twojego, throw expodczas gdy w drugim przypadku będzie poprawnie pochodzić z oryginalnego throwstwierdzenia.
CVn

2
Dlaczego wieszasz to Application.ThreadExceptionwydarzenie i otaczasz każdy wyjątek znakiem catch(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 exJest też bardzo, bardzo źle.
Keith

1
Zrozumiałem o catch (Exception ex) {throw ex; } jest bezużyteczny. Więc przypuszczam, że „zbędne” nie jest najlepszym słowem na określenie „Nie rób tego”. Dlatego zmieniłem trochę post, aby lepiej stwierdzić, że należy unikać dwóch pierwszych przykładów try catch.
Larry

3
Świetna i konstruktywna odpowiedź, przede wszystkim podobało mi się zdanie Only air :) I dzięki za to Application.ThreadExceptionwydarzenie nie zdawałem sobie z tego sprawy, bardzo przydatne.
Mahdi Tahsildari,


61

Najlepszą praktyką jest to, że obsługa wyjątków nigdy nie powinna ukrywać problemów . Oznacza to, że try-catchbloki powinny być niezwykle rzadkie.

Istnieją 3 okoliczności, w których użycie try-catchsensu ma sens.

  1. 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.

  2. Jeśli musisz zrobić coś na podstawie wyjątku (na przykład rejestrować lub wycofać transakcję), ponownie wyrzuć wyjątek.

  3. 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:

try 
{
    remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex) 
{
    // User's security details have expired
    return false;
}

return true;

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:

try
{
    DBConnection.Save();
}
catch
{
    // Roll back the DB changes so they aren't corrupted on ANY exception
    DBConnection.Rollback();

    // Re-throw the exception, it's critical that the user knows that it failed to save
    throw;
}

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:

try
{
    // Do something
}
catch(Exception ex) 
{
    // Log exception for developers
    WriteException2LogFile(ex);

    // Display message to users
    DisplayWarningBox("An error has occurred, please contact support!");
}

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 ( usingbloki, 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 usingbloków. Nie potrzebujesz ani jednego try-catchbloku.

TL; DR: Najlepszą praktyką jest w ogóle nie używanie try-catchbloków.


4
@Jorj powinieneś przeczytać cały post, a jeśli nadal się nie zgadzasz, być może bardziej konstruktywne byłoby przeciwstawienie się jednemu z moich argumentów wspierających niż stwierdzenie, że nie podoba ci się mój wniosek. Prawie zawsze istnieje lepszy wzór niż 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.
Keith,

Jak dotąd najlepsza odpowiedź - prawie każdy typ programowania .net ma jakiś HANDLER, który jest znacznie lepiej dostosowany do radzenia sobie z wyjątkami na poziomie globalnym, co znacznie ułatwia ich konsekwentną obsługę, a także znacznie łatwiejsze po prostu pozwolić, aby wybuchło w fazie rozwoju (dlaczego ktoś miałby chcieć przekopać plik dziennika do śledzenia stosu ??) @Kieth, prowadziłbym z twoim TLDR i dodałbym kilka przykładów globalnych programów obsługi (np. ThreadException, Application_Error itp.). Z całą
pewnością łapiemy

34

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.TryParsezamiast int.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:

  • ASP.NET: Błąd aplikacji Global.asax
  • Inne: zdarzenie AppDomain.FirstChanceException .

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:

  • Staraj się unikać wyjątków.
  • Jeśli nie jest to możliwe: procedury obsługi wyjątków pierwszej szansy.
  • Lub użyj aspektu PostSharp (AOP).

Odpowiadanie na @thewhiteambit w sprawie komentarza ...

@thewhiteambit powiedział:

Wyjątki nie są błędami krytycznymi, są wyjątkami! Czasami nie są nawet błędami, ale ich uznanie za błędy krytyczne jest całkowicie fałszywym zrozumieniem, czym są wyjątki.

Po pierwsze, jak wyjątek nie może być nawet błędem?

  • Brak połączenia z bazą danych => wyjątek.
  • Niepoprawny format ciągu, aby parsować jakiś wyjątek type =>
  • Próbowanie parsowania JSON i podczas gdy dane wejściowe nie są tak naprawdę JSON => wyjątkiem
  • Argument nullpodczas spodziewanego obiektu => wyjątek
  • Niektóre biblioteki mają błąd => zgłasza nieoczekiwany wyjątek
  • Jest połączenie z gniazdem, które się rozłącza. Następnie spróbuj wysłać wiadomość => wyjątek
  • ...

Moż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.

[...] uważają je za Fatalne błędy to całkowicie błędne zrozumienie wyjątków.

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

Na przykład w przypadku braku połączenia z bazą danych program może wyjątkowo kontynuować zapis do pliku lokalnego i wysłać zmiany do bazy danych, gdy będzie ona ponownie dostępna. Nieprawidłowy rzut ciąg-na-numer może zostać ponownie sparsowany przy użyciu lokalnej interpretacji wyjątków, tak jak podczas próby domyślnego języka angielskiego w analizie składni („1,5”) kończy się niepowodzeniem i ponowna próba z interpretacją niemiecką, która jest całkowicie dobrze, ponieważ używamy przecinka zamiast kropki jako separatora. Widzisz, że te wyjątki nie mogą nawet blokować, wymagają jedynie obsługi wyjątków.

  1. 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/catchjest 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 .

  2. 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 .

Nieobsługiwany wyjątek zwykle staje się błędem, ale same wyjątki nie są codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors

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:

[...] Wykorzystanie tych wyjątków do obsługi określonych błędów, które pojawiają się w celu kontynuacji programu, nazywa się kodowaniem w drodze wyjątku. Ten anty-wzór może szybko obniżyć wydajność oprogramowania i łatwość konserwacji.

Mówi także gdzieś:

Niepoprawne użycie wyjątku

Często kodowanie według wyjątku może prowadzić do dalszych problemów w oprogramowaniu z niewłaściwym użyciem wyjątku. Oprócz korzystania z obsługi wyjątków dla unikalnego problemu, niepoprawne użycie wyjątku przenosi to dalej, wykonując kod nawet po zgłoszeniu wyjątku. Ta zła metoda programowania przypomina metodę goto w wielu językach oprogramowania, ale występuje tylko po wykryciu problemu w oprogramowaniu.

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 ...

  • Twoja baza danych może przejść w tryb offline ...
  • Niektóre pliki można zablokować ...
  • Niektóre formatowanie może nie być obsługiwane ...
  • Sprawdzanie poprawności niektórych domen może się nie powieść ...
  • Twoja aplikacja powinna działać w trybie offline ...
  • niezależnie od przypadku użycia ...

... 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.


6

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ć.


6

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:

  • Dobrze rozumiesz, dlaczego może zostać zgłoszony wyjątek, i możesz wdrożyć określone odzyskiwanie, na przykład monitując użytkownika o wprowadzenie nowej nazwy pliku, gdy złapiesz obiekt FileNotFoundException.

  • Możesz utworzyć i wprowadzić nowy, bardziej szczegółowy wyjątek.

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}
  • Chcesz częściowo obsłużyć wyjątek przed przekazaniem go do dodatkowej obsługi. W poniższym przykładzie blok przechwytujący służy do dodania wpisu do dziennika błędów przed ponownym zgłoszeniem wyjątku.
    try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}

Proponuję przeczytać całą sekcję „ Wyjątki i obsługa wyjątków ”, a także Najlepsze praktyki dotyczące wyjątków .


1

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ć.


1

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:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}

1
Dzielenie przez zero wyjątków i tym podobnych lepiej jest rozwiązać, sprawdzając 0wcześniej licznik, a nie a try-catch. Także po co łapać Exceptiontutaj 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.
Keith,

Przeczytaj lepiej, co napisałem o podanym przeze mnie przykładzie - zwróć uwagę na „brak argumentów”. Oczywiście każdy kalkulator powinien zweryfikować podane argumenty. Mówiłem o środkowych krokach. W tym momencie weryfikacja argumentów użytkownika już się odbyła. Również w niektórych aplikacjach lepiej unikać wyjątków. Niektóre aplikacje powinny traktować wyjątki po cichu, podczas gdy inne powinny traktować wyjątki jako błędy. Na przykład serwer WWW powinien działać nawet w przypadku wystąpienia wyjątków, a oprogramowanie medyczne (na przykład aparaty rentgenowskie) powinno przerwać działanie w przypadku wystąpienia wyjątków.
Sorcerer86pt

Żadna aplikacja nigdy nie powinna traktować wyjątków w ciszy. Czasami masz wyjątek, który może obsłużyć kod, ale takie użycie powinno być zarówno rzadkie, jak i specyficzne dla oczekiwanego wyjątku. Twój przykład serwera WWW jest kiepski - powinien mieć ustawienia konfiguracyjne, które pozwolą ci wybrać sposób rejestrowania błędów i tego, czy będą wyświetlane ze szczegółami, czy tylko stronę HTTP 500, ale nigdy nie powinny cicho ignorować błędów.
Keith

Próbuję dowiedzieć się, co naprawdę motywuje ludzi do dodania jeszcze jednego synonimu „goto”. Ale w odniesieniu do dzielenia przez zero byłby to JEDEN typ wyjątku, który widzę, który uzasadnia ulepszenie języka. Czemu? Ponieważ może się zdarzyć, że A) zero jest statystycznie nieskończenie małe w zbiorze danych, a B) użycie (dopuszczenie) wyjątku może być znacznie bardziej wydajne, ponieważ wykonanie podziału jest jednym ze sposobów sprawdzenia dzielnika zerowego. Kiedy A i B są prawdziwe, średnie wykonanie twojego programu będzie szybsze z wykorzystaniem wyjątku, a może nawet być mniejsze.
Mike Layton

1

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ć.

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);//it will write the or log the error in a text file
}

Polecam wybrać drugie podejście do całej aplikacji.


2
Drugie podejście nie pokazuje użytkownikowi, że wystąpił błąd - na przykład, jeśli zapisywali coś, nie wiedzieliby, że to się nie udało. catchbloki powinny zawsze wywoływać throwwyją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źć.
Keith

0

Najgorszą rzeczą jest pozostawienie pustego bloku catch. Jeśli wystąpi błąd, najlepszym sposobem na jego rozwiązanie jest:

  1. Zaloguj się do pliku \ bazy danych itp.
  2. Spróbuj to naprawić w locie (być może próbujesz alternatywnego sposobu wykonania tej operacji)
  3. Jeśli nie możemy tego naprawić, powiadom użytkownika, że ​​wystąpił jakiś błąd i oczywiście przerwij operację

0

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ą.


0

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ć

  1. Polegasz na komponencie innej firmy i chcesz kontynuować program w przypadku błędu.
  2. Masz uzasadnienie biznesowe, które musisz kontynuować w przypadku błędu

6
Nie wolno nie rzucać Exception. Zawsze. Rzuć odpowiednią podklasę, Exceptionjakiej chcesz, ale nigdy, Exceptionponieważ nie daje to absolutnie żadnych informacji semantycznych. Całkowicie nie widzę scenariusza, w którym sensowne jest rzucanie, Exceptionale nie jego podklasa.
CVn


0

catchBez 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, FileNotFoundExceptiona następnie na samym końcu powinieneś złapać, Exceptionktóry przechwyciłby każdy inny wyjątek i zarejestrowałby je.


Dlaczego generał catch(Exception)na końcu? Jeśli się tego nie spodziewasz, zawsze najlepszym rozwiązaniem jest przekazanie go do następnej warstwy.
Keith

1
@Keith tak masz rację ... nie ma sensu wychwytywać wyjątków, których się nie spodziewasz, ale możesz być ogólnym wyjątkiem dla celów logowania.
Anirudha

0

Czasami musisz traktować wyjątki, które nic nie mówią użytkownikom.

Moja droga to:

  • Aby wyłapać nieumiejętne wyjątki na poziomie aplikacji (np. W global.asax) dla krytycznych wyjątków (aplikacja nie może być przydatna). Te wyjątki nie łapię na tym miejscu. Po prostu zaloguj je na poziomie aplikacji i pozwól systemowi wykonać swoją pracę.
  • Złap „na miejscu” i pokaż użyteczne informacje dla użytkownika (podany zły numer, nie można go przeanalizować).
  • Złap się na miejscu i nie rób nic z marginalnych problemów, takich jak: „Sprawdzę dostępność aktualizacji w tle, ale usługa nie działa”.

To zdecydowanie nie musi być najlepsza praktyka. ;-)


0

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:

int Parse(string input);

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.