Po co używać try… wreszcie bez klauzuli catch?

79

Klasyczny sposób programowania to try ... catch. Kiedy stosować trybez catch?

W Pythonie następujące elementy wydają się zgodne z prawem i mogą mieć sens:

try:
  #do work
finally:
  #do something unconditional

Jednak kod nic nie catchzrobił. Podobnie można by pomyśleć w Javie:

try {
    //for example try to get a database connection
}
finally {
  //closeConnection(connection)
}

Wygląda dobrze i nagle nie muszę się martwić typami wyjątków itp. Jeśli to dobra praktyka, to kiedy to dobra praktyka? Alternatywnie, jakie są powody, dla których nie jest to dobra praktyka lub jest niezgodna z prawem? (Nie skompilowałem źródła. Pytam o to, ponieważ może to być błąd składniowy Java. Sprawdziłem, czy Python na pewno się skompiluje.)

Powiązany problem, z którym się zetknąłem: kontynuuję pisanie funkcji / metody, na końcu której musi coś zwrócić. Może jednak znajdować się w miejscu, do którego nie należy dotrzeć i musi być punktem powrotu. Tak więc, nawet jeśli obsłużę powyższe wyjątki, wciąż zwracam NULLciąg znaków lub pusty ciąg w pewnym momencie kodu, który nie powinien zostać osiągnięty, często na końcu metody / funkcji. Zawsze udawało mi się zrestrukturyzować kod, aby nie musiał return NULL, bo to absolutnie wydaje się mniej niż dobrą praktyką.

Niklas Rosencrantz
źródło
4
W Javie dlaczego nie umieścić instrukcji return na końcu bloku try?
kevin cline
2
Przykro mi, że try..finally i try..catch używają słowa kluczowego try, oprócz obu zaczynających się od try, są to 2 zupełnie różne konstrukcje.
Pieter B
3
try/catchnie jest „klasycznym sposobem programowania”. Jest to klasyczny sposób programowania w C ++ , ponieważ w C ++ brakuje właściwej konstrukcji try / last, co oznacza, że ​​musisz zaimplementować gwarantowane odwracalne zmiany stanu przy użyciu brzydkich hacków z udziałem RAII. Ale przyzwoite języki OO nie mają tego problemu, ponieważ zapewniają try / wreszcie. Jest używany do zupełnie innych celów niż try / catch.
Mason Wheeler,
3
Często to widzę w przypadku zewnętrznych zasobów połączeń. Chcesz wyjątku, ale musisz upewnić się, że nie pozostawiasz otwartego połączenia itp. Jeśli go złapiesz, w niektórych przypadkach po prostu wrzucisz go do następnej warstwy.
Rig
4
@MasonWheeler „Brzydkie hacki”, proszę wyjaśnij, co jest złego w tym, że obiekt ma własną funkcję czyszczenia?
Baldrickk

Odpowiedzi:

146

Zależy to od tego, czy poradzisz sobie z wyjątkami, które mogą zostać podniesione w tym momencie, czy nie.

Jeśli możesz poradzić sobie z wyjątkami lokalnie, powinieneś i lepiej jest obsłużyć błąd jak najbliżej miejsca, w którym został zgłoszony.

Jeśli nie możesz poradzić sobie z nimi lokalnie, to posiadanie try / finallybloku jest całkowicie uzasadnione - zakładając, że jest jakiś kod, który musisz wykonać niezależnie od tego, czy metoda się powiodła, czy nie. Na przykład (z komentarza Neila ) otwarcie strumienia, a następnie przekazanie tego strumienia do wewnętrznej metody, która ma zostać załadowana, jest doskonałym przykładem tego, kiedy będziesz potrzebować try { } finally { }, używając klauzuli last, aby upewnić się, że strumień jest zamknięty niezależnie od sukcesu lub błąd odczytu.

Jednak nadal będziesz potrzebować modułu obsługi wyjątków gdzieś w kodzie - chyba że chcesz, aby aplikacja całkowicie uległa awarii. Zależy to od architektury aplikacji dokładnie tam, gdzie jest ten moduł obsługi.

ChrisF
źródło
8
„i lepiej obsłużyć błąd jak najbliżej miejsca, w którym został zgłoszony.” to zależy. Jeśli możesz się zregenerować i nadal ukończyć misję (że tak powiem), tak, oczywiście. Jeśli nie możesz, lepiej pozwolić, aby wyjątek przeszedł na sam szczyt, gdzie (prawdopodobna) interwencja użytkownika będzie wymagana, aby poradzić sobie z tym, co się stało.
Czy
13
@will - dlatego użyłem wyrażenia „jak to możliwe”.
ChrisF
8
Dobra odpowiedź, ale chciałbym dodać przykład: Otwarcie strumienia i przekazanie tego strumienia do wewnętrznej metody, która ma zostać załadowana, jest doskonałym przykładem tego, kiedy trzeba try { } finally { }, korzystając z klauzuli last, aby upewnić się, że strumień zostanie ostatecznie zamknięty bez względu na sukces / niepowodzenie.
Neil
ponieważ czasami cała droga na górę jest tak blisko, jak to tylko możliwe
Newtopian
„po prostu próba / wreszcie blok jest całkowicie uzasadnione” szukałem właśnie tej odpowiedzi. dziękuję @ChrisF
Neal Aise
36

finallyBlok służy do kodu, który zawsze musi uruchomić, czy warunek błędu (wyjątek) doszło, czy nie.

Kod w finallybloku jest uruchamiany po zakończeniu trybloku, a jeśli wystąpił wyjątek, po zakończeniu odpowiedniego catchbloku. Jest zawsze uruchamiany, nawet jeśli wystąpił nieprzechwycony wyjątek w bloku trylub catch.

finallyBlok jest zazwyczaj używany do plików zamykających, połączeń sieciowych itp, które zostały otwarte w trybloku. Powodem jest to, że połączenie pliku lub sieci musi zostać zamknięte, niezależnie od tego, czy operacja przy użyciu tego pliku lub połączenia sieciowego zakończyła się powodzeniem, czy też nie.

W finallybloku należy zadbać o to, aby sam nie zgłosił wyjątku. Na przykład podwójnie sprawdź wszystkie zmienne pod kątem nullitp.

yfeldblum
źródło
8
+1: To idiomatyczne dla „musi zostać oczyszczone”. Większość zastosowań try-finallymożna zastąpić withoświadczeniem.
S.Lott,
12
Różne języki mają niezwykle przydatne ulepszenia specyficzne dla danego języka try/finally. C # ma using, Python ma withitp.
yfeldblum
5
@yfeldblum - istnieje subtelna różnica między usingi try-finally, ponieważ Disposemetoda nie będzie wywoływana przez usingblok, jeśli wystąpi wyjątek w konstruktorze IDisposableobiektu. try-finallypozwala na wykonanie kodu, nawet jeśli konstruktor obiektu zgłasza wyjątek.
Scott Whitlock,
5
@ScottWhitlock: To dobra rzecz? Co próbujesz zrobić, wywołać metodę na nieskonstruowanym obiekcie? To miliard rodzajów złych.
DeadMG,
1
@DeadMG: Zobacz także stackoverflow.com/q/14830534/18192
Brian
17

Przykładem, w którym try ... wreszcie bez klauzuli catch jest właściwe (a nawet idiomatyczne ) w Javie, jest użycie blokady w równoległym pakiecie blokad narzędzi.

  • Oto jak zostało to wyjaśnione i uzasadnione w dokumentacji API (pogrubiona czcionka w cytacie jest moja):

    ... Brak blokowania strukturalnego blokuje automatyczne zwolnienie blokad, które występuje przy zsynchronizowanych metodach i instrukcjach. W większości przypadków należy zastosować następujący idiom :

     Lock l = ...;
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }

    Gdy blokowanie i odblokowywanie odbywa się w różnych zakresach, należy zadbać o to, aby cały kod, który jest wykonywany, gdy blokada jest utrzymywana, był chroniony przez try-wreszcie lub try-catch, aby upewnić się, że blokada zostanie zwolniona w razie potrzeby .

komar
źródło
Czy mogę włożyć l.lock()próbę? try{ l.lock(); }finally{l.unlock();}
RMachnik
1
technicznie możesz. Nie umieściłem go tam, ponieważ semantycznie ma to mniej sensu. tryw tym fragmencie ma na celu zawinąć dostęp do zasobów, po co zanieczyszczać go czymś niezwiązanym z tym
komentuj
4
Możesz, ale jeśli się l.lock()nie powiedzie, finallyblok będzie nadal działał, jeśli l.lock()jest w trybloku. Jeśli zrobisz to tak, jak sugeruje komar, finallyblok uruchomi się tylko wtedy, gdy wiemy, że zamek został przejęty.
Wtrmute,
12

Na poziomie podstawowym catchi finallyrozwiązać dwa problemy związane jednak różne:

  • catch służy do obsługi problemu zgłoszonego przez wywołany kod
  • finally służy do czyszczenia danych / zasobów utworzonych / zmodyfikowanych przez bieżący kod, bez względu na to, czy wystąpił problem, czy nie

Oba są więc w jakiś sposób powiązane z problemami (wyjątkami), ale to prawie wszystko, co ich łączy.

Ważną różnicą jest to, że finallyblok musi być w tej samej metodzie, w której utworzono zasoby (aby uniknąć wycieku zasobów) i nie może być umieszczony na innym poziomie w stosie wywołań.

Jest catchto jednak inna sprawa: właściwe miejsce zależy od tego, gdzie rzeczywiście można obsłużyć wyjątek. Nie ma sensu łapać wyjątku w miejscu, w którym nie można nic z tym zrobić, dlatego czasem lepiej po prostu pozwolić mu upaść.

Joachim Sauer
źródło
2
Nitpick: „... ostatecznie blok musi być w tej samej metodzie, w której utworzono zasoby ...” . Z pewnością dobrym pomysłem jest zrobienie tego w ten sposób, ponieważ łatwiej jest zauważyć, że nie ma wycieku zasobów. Nie jest to jednak konieczny warunek wstępny; tzn. nie musisz tego robić w ten sposób. Możesz zwolnić zasób w finallyzałączonej instrukcji try (statycznej lub dynamicznej) ... i nadal być w 100% szczelny.
Stephen C
6

@yfeldblum ma poprawną odpowiedź: spróbuj na końcu bez instrukcji catch zwykle należy zastąpić odpowiednią konstrukcją językową.

W C ++ używa RAII i konstruktorów / destruktorów; w Pythonie jest to withinstrukcja; a w języku C # jest to usingstwierdzenie.

Są one prawie zawsze bardziej eleganckie, ponieważ kod inicjalizacji i finalizacji znajdują się w jednym miejscu (obiekt abstrakcyjny), a nie w dwóch miejscach.

Neil G.
źródło
2
A w Javie są to bloki ARM
MarkJ
2
Załóżmy, że masz źle zaprojektowany obiekt (na przykład taki, który nie implementuje odpowiednio IDisposable w C #), który nie zawsze jest wykonalną opcją.
mootinator
1
@mootinator: nie możesz dziedziczyć po źle zaprojektowanym obiekcie i naprawić go?
Neil G,
2
Czy kapsułkowanie? Bah. Oczywiście, że tak.
mootinator
4

W wielu językach finallypo instrukcji return działa także instrukcja. Oznacza to, że możesz zrobić coś takiego:

try {
  // Do processing
  return result;
} finally {
  // Release resources
}

Które zwalniają zasoby bez względu na to, jak metoda została zakończona za pomocą wyjątku lub zwykłej instrukcji return.

To, czy jest to dobre czy złe, jest przedmiotem dyskusji, ale try {} finally {}nie zawsze ogranicza się do obsługi wyjątków.

Kamiel Wanrooij
źródło
0

Mógłbym wywołać gniew Pythonistów (nie wiem, ponieważ nie używam dużo Pythona) lub programistów z innych języków z tą odpowiedzią, ale moim zdaniem większość funkcji nie powinna mieć catchbloku, najlepiej mówiąc. Aby pokazać dlaczego, pozwólcie, że skontrastuję to z ręcznym rozpowszechnianiem kodu błędu, takiego jaki musiałem zrobić podczas pracy z Turbo C na przełomie lat 80. i 90.

Powiedzmy, że mamy funkcję ładowania obrazu lub czegoś podobnego w odpowiedzi na wybór przez użytkownika pliku obrazu do załadowania, a jest to napisane w C i asemblerze:

wprowadź opis zdjęcia tutaj

Pominąłem niektóre funkcje niskiego poziomu, ale widzimy, że zidentyfikowałem różne kategorie funkcji, oznaczone kolorami, w zależności od ich obowiązków związanych z obsługą błędów.

Punkt awarii i powrotu do zdrowia

Teraz nigdy nie było trudno napisać kategorie funkcji, które nazywam „możliwym punktem awarii” (tymi, które throwtzn.) Oraz funkcjami „odzyskiwania i raportowania błędów” (tymi, które catchtj.).

Funkcje te zawsze były trywialne, aby poprawnie pisać, zanim dostępna była obsługa wyjątków, ponieważ funkcja, która może napotkać awarię zewnętrzną, na przykład brak alokacji pamięci, może po prostu zwrócić a NULLlub 0lub-1 lub ustawić globalny kod błędu lub coś w tej sprawie. Odzyskiwanie / raportowanie błędów zawsze było łatwe, ponieważ po przejściu przez stos wywołań do punktu, w którym sensowne było odzyskiwanie i zgłaszanie awarii, wystarczy wziąć kod błędu i / lub komunikat i zgłosić go użytkownikowi. I naturalnie funkcja u progu tej hierarchii, która nigdy, nigdy nie może zawieść, bez względu na to, jak zostanie zmieniona w przyszłości ( Convert Pixel), jest bardzo prosta do napisania poprawnie (przynajmniej w odniesieniu do obsługi błędów).

Propagacja błędów

Jednak żmudnymi funkcjami podatnymi na błędy ludzkie były propagatory błędów , te, które nie popadły bezpośrednio w awarię, ale wywołały funkcje, które mogłyby zawieść gdzieś głębiej w hierarchii. W tym momencie Allocate Scanlinebyć może trzeba będzie obsłużyć awarię, malloca następnie zwrócić błąd do Convert Scanlines, a następnie Convert Scanlinesbędzie musiał sprawdzić ten błąd i przekazać go do Decompress Image, a następnie Decompress Image->Parse Image, iParse Image->Load Image , iLoad Image na polecenie użytkownika końcowego, gdzie błąd jest wreszcie zgłoszonych .

To jest miejsce, w którym wielu ludzi popełnia błędy, ponieważ tylko jeden propagator błędów nie może sprawdzić i przekazać błędu, aby cała hierarchia funkcji upadła, jeśli chodzi o prawidłowe zarządzanie błędem.

Ponadto, jeśli kody błędów są zwracane przez funkcje, prawie tracimy zdolność, powiedzmy, 90% naszej bazy kodów, do zwracania interesujących wartości po sukcesie, ponieważ tak wiele funkcji musiałoby zarezerwować swoją wartość zwrotną na zwrócenie kodu błędu na niepowodzenie .

Ograniczanie błędów ludzkich: globalne kody błędów

Jak więc zmniejszyć prawdopodobieństwo błędu ludzkiego? Tutaj mogę nawet wywołać gniew niektórych programistów C, ale moim zdaniem natychmiastową poprawą jest użycie globalne kodów błędów, takich jak OpenGL glGetError. To przynajmniej uwalnia funkcje do zwracania znaczących wartości zainteresowania w przypadku sukcesu. Istnieją sposoby, aby uczynić ten wątek bezpiecznym i wydajnym, gdy kod błędu jest zlokalizowany w wątku.

Istnieją również przypadki, w których funkcja może napotkać błąd, ale jest względnie nieszkodliwy, aby działał nieco dłużej, zanim powróci przedwcześnie w wyniku wykrycia poprzedniego błędu. Pozwala to na coś takiego bez konieczności sprawdzania błędów w porównaniu z 90% wywołań funkcji wykonanych w każdej pojedynczej funkcji, dzięki czemu może nadal umożliwiać właściwą obsługę błędów, nie będąc tak drobiazgowym.

Ograniczanie błędów ludzkich: obsługa wyjątków

Jednak powyższe rozwiązanie wciąż wymaga tak wielu funkcji do radzenia sobie z aspektem przepływu sterowania ręcznej propagacji błędów, nawet jeśli mogłoby to zmniejszyć liczbę wierszy if error happened, return errorkodu typu ręcznego . Nie wyeliminuje go całkowicie, ponieważ często musi istnieć co najmniej jedno miejsce sprawdzające błąd i zwracające prawie każdą funkcję propagacji błędu. Dlatego właśnie pojawia się obsługa wyjątków, aby uratować dzień (sorta).

Ale wartością obsługi wyjątków jest tutaj uwolnienie potrzeby radzenia sobie z aspektem przepływu sterowania ręcznej propagacji błędów. Oznacza to, że jego wartość jest związana ze zdolnością do unikania konieczności pisania mnóstwa catchbloków w całej bazie kodu. Na powyższym diagramie jedynym miejscem, w którym powinien znajdować się catchblok, jest miejsce Load Image User Commandzgłoszenia błędu. Nic innego nie powinno idealnie mieć do catchniczego, ponieważ w przeciwnym razie zaczyna być tak nudne i podatne na błędy, jak obsługa kodów błędów.

Więc jeśli mnie spytasz, jeśli masz bazę kodów, która naprawdę korzysta z obsługi wyjątków w elegancki sposób, powinna mieć minimalną liczbę catchbloków (przez minimum nie mam na myśli zera, ale bardziej podobną do jednego dla każdego unikalnego wysokiego - operacja użytkownika końcowego, która może zakończyć się niepowodzeniem, a być może nawet mniej, jeśli wszystkie operacje użytkownika wysokiej klasy zostaną wywołane przez centralny system poleceń.

Oczyszczanie zasobów

Jednak obsługa wyjątków rozwiązuje jedynie potrzebę unikania ręcznego radzenia sobie z aspektami przepływu sterowania propagacją błędów w wyjątkowych ścieżkach, oddzielnych od normalnych przepływów wykonania. Często funkcja, która służy jako propagator błędów, nawet jeśli robi to teraz automatycznie z EH, może nadal zdobywać zasoby, które musi zniszczyć. Na przykład taka funkcja może otworzyć plik tymczasowy, który musi zamknąć przed powrotem z funkcji bez względu na wszystko, lub zablokować muteks, który musi odblokować bez względu na wszystko.

W tym celu mógłbym wywołać gniew wielu programistów z różnych języków, ale myślę, że podejście C ++ do tego jest idealne. Język wprowadza niszczyciele, które są wywoływane w sposób deterministyczny, gdy tylko obiekt wychodzi poza zakres. Z tego powodu kod C ++, który, powiedzmy, blokuje muteks przez obiekt muteksu o zasięgu z destruktorem, nie musi go ręcznie odblokowywać, ponieważ zostanie automatycznie odblokowany, gdy obiekt wyjdzie poza zakres, bez względu na to, co się stanie (nawet jeśli wyjątek jest napotkane). Tak więc naprawdę nie ma potrzeby, aby dobrze napisany kod C ++ miał kiedykolwiek do czynienia z czyszczeniem zasobów lokalnych.

W językach, w których brakuje destruktorów, może być konieczne użycie finallybloku do ręcznego wyczyszczenia lokalnych zasobów. To powiedziawszy, w dalszym ciągu bije konieczność zaśmiecania kodu ręczną propagacją błędów, pod warunkiem , że nie musisz robić catchwyjątków w całym dziwacznym miejscu.

Odwracanie zewnętrznych efektów ubocznych

To najtrudniejszym problemem do rozwiązania koncepcyjne. Jeśli jakakolwiek funkcja, niezależnie od tego, czy jest to propagator błędów, czy punkt awarii, powoduje zewnętrzne skutki uboczne, musi cofnąć lub „cofnąć” te skutki uboczne, aby przywrócić system do stanu, jakby operacja nigdy nie wystąpiła, zamiast „ połowa ważna ”stan, w którym operacja zakończyła się w połowie. Nie znam języków, które znacznie ułatwiają ten problem pojęciowy, oprócz języków, które po prostu zmniejszają potrzebę wywoływania przez większość funkcji zewnętrznych skutków ubocznych, takich jak języki funkcjonalne, które obracają się wokół niezmienności i trwałych struktur danych.

Tu finallyjest niewątpliwie jednym z najbardziej eleganckich rozwiązań tam problemu w językach krążących wokół zmienność i skutków ubocznych, ponieważ często ten rodzaj logiki jest bardzo specyficzny dla danej funkcji i nie mapa tak dobrze do koncepcji „oczyszczania zasobów „. I zalecam finallyswobodne stosowanie w tych przypadkach, aby upewnić się, że twoja funkcja odwraca skutki uboczne w językach, które ją obsługują, niezależnie od tego, czy potrzebujesz catchbloku (i ponownie, jeśli mnie zapytasz, dobrze napisany kod powinien mieć minimalną liczbę catchbloki, a wszystkie catchbloki powinny znajdować się w miejscach, w których jest to najbardziej sensowne, jak na schemacie powyżej w Load Image User Command).

Wymarzony język

Jednak IMO finallyjest bliska ideału do odwrócenia skutków ubocznych, ale nie do końca. Musimy wprowadzić jedną booleanzmienną, aby skutecznie cofnąć skutki uboczne w przypadku przedwczesnego wyjścia (z wyjątku rzuconego lub w inny sposób), tak jak:

bool finished = false;
try
{
    // Cause external side effects.
    ...

    // Indicate that all the external side effects were
    // made successfully.
    finished = true; 
}
finally
{
    // If the function prematurely exited before finishing
    // causing all of its side effects, whether as a result of
    // an early 'return' statement or an exception, undo the
    // side effects.
    if (!finished)
    {
        // Undo side effects.
        ...
    }
}

Gdybym kiedykolwiek mógł zaprojektować język, moim wymarzonym sposobem rozwiązania tego problemu byłoby zautomatyzowanie powyższego kodu:

transaction
{
    // Cause external side effects.
    ...
}
rollback
{
    // This block is only executed if the above 'transaction'
    // block didn't reach its end, either as a result of a premature
    // 'return' or an exception.

    // Undo side effects.
    ...
}

... z destruktorów zautomatyzować oczyszczanie lokalnych zasobów, co czyni go tak tylko trzeba transaction, rollbacki catch(choć może nadal chcę dodać finally, powiedzmy, współpracując z zasobów C, które nie mycia się w górę). Jednak finallyze booleanzmiennej jest najbliższa rzecz do podejmowania to proste, że znalazłem do tej pory brakowało mój wymarzony język. Drugim najprostszym rozwiązaniem, jakie znalazłem w tym zakresie, jest osłona zakresu w takich językach, jak C ++ i D, ale zawsze uważałem, że osłona zakresu jest nieco niewygodna koncepcyjnie, ponieważ zaciera ideę „czyszczenia zasobów” i „odwracania skutków ubocznych”. Moim zdaniem są to bardzo różne pomysły, którymi należy się zająć w inny sposób.

Moje małe marzenie o języku obracałoby się również wokół niezmienności i trwałych struktur danych, aby ułatwić, choć nie jest to konieczne, pisanie wydajnych funkcji, które nie muszą głęboko kopiować masywnych struktur danych w całości, nawet jeśli funkcja ta powoduje bez skutków ubocznych.

Wniosek

W każdym razie, pomijając moje wędrówki, myślę, że twój try/finallykod do zamykania gniazda jest w porządku i świetny, biorąc pod uwagę, że Python nie ma odpowiednika destruktorów w C ++, i osobiście uważam, że powinieneś używać go swobodnie w miejscach, które wymagają odwrócenia efektów ubocznych i zminimalizować liczbę miejsc, w których musisz, catchdo miejsc, w których jest to najbardziej sensowne.


źródło
-66

Łapanie błędów / wyjątków i porządne postępowanie z nimi jest wysoce zalecane, nawet jeśli nie jest to obowiązkowe.

Powodem, dla którego to mówię, jest to, że uważam, że każdy programista powinien znać zachowanie swojej aplikacji i zająć się nią, w przeciwnym razie nie ukończyłby należycie swojej pracy. Nie ma sytuacji, w której blok „spróbuj nareszcie” zastąpi blok „spróbuj nareszcie”.

Dam ci prosty przykład: Załóżmy, że napisałeś kod do przesyłania plików na serwer bez wychwytywania wyjątków. Teraz, jeśli z jakiegoś powodu przesyłanie nie powiedzie się, klient nigdy nie będzie wiedział, co poszło źle. Ale jeśli złapałeś wyjątek, możesz wyświetlić zgrabny komunikat o błędzie wyjaśniający, co poszło nie tak i jak użytkownik może to naprawić.

Złota zasada: zawsze wychwytuj wyjątek, ponieważ zgadywanie wymaga czasu

Pankaj Upadhyay
źródło
22
-1: W Javie może być potrzebna klauzula wreszcie, aby zwolnić zasoby (np. Zamknąć plik lub zwolnić połączenie DB). Jest to niezależne od zdolności do obsługi wyjątku.
kevin cline
@ kevincline, On nie pyta, czy użyć w końcu, czy nie ... Wszystko, co pyta, to czy złapanie wyjątku jest wymagane, czy nie ... Wie, co próbuje, złap i wreszcie robi ... najważniejsza część, wszyscy to wiemy i dlaczego jest używana ....
Pankaj Upadhyay
63
@Pankaj: Twoja odpowiedź sugeruje, że catchklauzula powinna być zawsze obecna, ilekroć istnieje try. Bardziej doświadczeni współpracownicy, w tym ja, uważają, że to kiepska rada. Twoje rozumowanie jest błędne. Metoda zawierająca tryto nie jedyne możliwe miejsce, w którym można wychwycić wyjątek. Często najprostszym i najlepszym jest zezwolenie na łapanie i zgłaszanie wyjątków od najwyższego poziomu, zamiast kopiowania klauzul catch w całym kodzie.
kevin cline
@kevincline, uważam, że postrzeganie pytania z drugiej strony jest nieco inne. Pytanie brzmiało dokładnie, czy powinniśmy złapać wyjątek, czy nie ... I odpowiedziałem w tym względzie. Obsługa wyjątków może odbywać się na wiele sposobów, a nie tylko próbować w końcu. Ale nie o to chodziło w OP. Jeśli zgłoszenie wyjątku jest wystarczające dla ciebie i innych facetów, muszę powiedzieć, że powodzenia.
Pankaj Upadhyay
Z wyjątkami chcesz, aby normalne wykonywanie instrukcji zostało przerwane (i bez ręcznego sprawdzania powodzenia na każdym kroku). Dzieje się tak szczególnie w przypadku głęboko zagnieżdżonych wywołań metod - metoda 4 warstw w bibliotece nie może po prostu „połknąć” wyjątku; należy go wyrzucić przez wszystkie warstwy. Oczywiście każda warstwa może zawinąć wyjątek i dodać dodatkowe informacje; często nie robi się tego z różnych powodów, dodatkowy czas na rozwój i niepotrzebna gadatliwość to dwa największe.
Daniel B,