Dlaczego try {...} finally { ... } good; try { ... } catch {} bad?

Widziałem, jak ludzie mówią, że używanie catch bez argumentów jest złą formą, zwłaszcza jeśli ten catch nic nie robi: {]}

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Jest to jednak uważane za dobrą formę:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Z tego co wiem, to jedyna różnica między umieszczeniem kodu porządkowego w bloku finalnym a umieszczeniem kodu porządkowego po próbie..catch blocks jest, jeśli masz instrukcje return w bloku try (w takim przypadku kod czyszczenia w końcu zostanie uruchomiony, ale kod po próbie..Wola łapska nie).

W Przeciwnym Razie, co jest takiego specjalnego w końcu?

Author: FMFF, 2008-09-24

20 answers

Duża różnica polega na tym, że try...catch połknie wyjątek, ukrywając fakt, że wystąpił błąd. try..finally uruchomi Twój kod oczyszczający, a następnie wyjątek będzie działał dalej, aby był obsługiwany przez coś, co wie, co z nim zrobić.

 331
Author: Khoth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:12:51

"Finally" jest stwierdzeniem "coś, co zawsze musisz zrobić, aby upewnić się, że stan programu jest zdrowy". W związku z tym, zawsze jest dobra forma, aby mieć taki, jeśli jest jakakolwiek możliwość, że wyjątki mogą zrzucić stan programu. Kompilator dokłada również wszelkich starań, aby zapewnić, że Twój kod w końcu zostanie uruchomiony.

"Catch" jest stwierdzeniem "mogę odzyskać z tego wyjątku". Powinieneś odzyskać tylko od wyjątków, które naprawdę możesz poprawić-catch bez argumentów mówi " Hej, mogę odzyskać od cokolwiek!", co niemal zawsze jest nieprawdą.

gdyby było możliwe odzyskanie po każdym wyjątku, to byłoby to naprawdę semantyczne sprzeczki, o to, czym deklarujesz zamiar. Jednak tak nie jest i prawie na pewno ramki powyżej twojego będą lepiej przygotowane do obsługi pewnych wyjątków. W związku z tym użyj wreszcie, aby uruchomić kod czyszczenia za darmo, ale nadal pozwól, aby więcej kompetentnych handlowców poradziło sobie z tym problemem.

 56
Author: Adam Wright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:43:33

Ponieważ kiedy ta jedna linia rzuca wyjątek, nie wiedziałbyś o tym.

Z pierwszym blokiem kodu, wyjątkiem będzie po prostu , program będzie nadal działał nawet wtedy, gdy stan programu może być zły.

Z drugim blokiem, wyjątek będzie wyrzucony i bąbelkowy w górę , ale reader.Close() jest nadal gwarantowany.

Jeśli wyjątek nie jest oczekiwany, nie próbuj../ align = "left" / być trudne do debugowania później, gdy program wszedł w złym stanie i nie masz pojęcia, dlaczego.

 32
Author: chakrit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:23

W końcu jest wykonywany bez względu na wszystko. Tak więc, jeśli twój blok próbny powiódł się, zostanie on uruchomiony, jeśli twój blok próbny zawiedzie, następnie wykona blok catch, a następnie blok finally.

Ponadto, lepiej jest spróbować użyć następującej konstrukcji:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Ponieważ instrukcja using jest automatycznie zawijana w try / finally I strumień zostanie automatycznie zamknięty. (Będziesz musiał umieścić try / catch wokół instrukcji użycia, jeśli chcesz rzeczywiście złapać wyjątek).

 20
Author: Mark Ingram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-20 13:40:40

Podczas gdy kolejne 2 bloki kodu są równoważne, nie są równe.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 'finally' jest kodem ujawniającym intencje. Deklarujesz kompilatorowi i innym programistom, że ten kod musi działać bez względu na wszystko.
  2. Jeśli masz wiele bloków catch i masz kod czyszczenia, musisz w końcu. Bez końca powielałbyś swój kod oczyszczający w każdym bloku catch. (Zasada sucha)

Wreszcie klocki są wyjątkowe. CLR rozpoznaje i leczy kod z finally block oddzielony od catch blocks, a CLR dokłada wszelkich starań, aby zagwarantować, że finally block będzie zawsze wykonywany. To nie jest tylko cukier składniowy z kompilatora.

 6
Author: Robert Paulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 20:55:21

Zgadzam się z tym, co tutaj wydaje się być konsensusem-pusty 'catch' jest zły, ponieważ maskuje jakikolwiek wyjątek, który mógł wystąpić w bloku try.

Również, z punktu widzenia czytelności, kiedy widzę blok 'try', zakładam, że pojawi się odpowiednie polecenie 'catch'. Jeśli używasz tylko "try", aby upewnić się, że zasoby są dealowane w bloku "finally", możesz zamiast tego rozważyć 'using' polecenie :

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Możesz użyć polecenia 'using' z dowolnym obiektem, który implementuje IDisposable. Metoda dispose () obiektu zostanie wywołana automatycznie na końcu bloku.

 5
Author: Chris Lawlor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:28:06

Próba..ostatecznie block nadal będzie rzucał wszelkie wyjątki, które zostały podniesione. Wszystko, co robi finally, to upewnienie się, że kod czyszczenia jest uruchamiany przed wyrzuceniem wyjątku.

Próba..catch z pustym catch całkowicie pochłonie każdy wyjątek i ukryje fakt, że tak się stało. Czytelnik będzie zamknięty, ale nie wiadomo, czy coś się stało. Co jeśli twoim zamiarem było napisanie i do pliku? W takim przypadku nie dotrzesz do tej części kodu i myfile.txt {[7] } będzie pusty. Czy wszystkie dalsze metody radzą sobie z tym prawidłowo? Kiedy zobaczysz pusty plik, czy będziesz w stanie poprawnie odgadnąć, że jest pusty, ponieważ został wyrzucony wyjątek? Lepiej wyrzucić wyjątek i dać znać, że robisz coś złego. Innym powodem jest próba..łapanie w ten sposób jest całkowicie błędne. To, co mówisz, robiąc to, to: "bez względu na to, co się stanie, poradzę sobie z tym."A co z StackOverflowException, można posprzątać potem? A co z OutOfMemoryException? Ogólnie rzecz biorąc, powinieneś obsługiwać tylko wyjątki, których oczekujesz i wiedzieć, jak sobie z nimi radzić.
 3
Author: OwenP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:16:01

Jeśli nie wiesz, jaki typ wyjątku złapać lub co z nim zrobić, nie ma sensu mieć instrukcji catch. Powinieneś po prostu zostawić go dla wyższego rozmówcy, który może mieć więcej informacji na temat sytuacji, aby wiedzieć, co robić.

W przypadku wystąpienia wyjątku nadal powinieneś mieć tam wyrażenie finally, aby móc wyczyścić zasoby, zanim wyjątek zostanie rzucony do wywołującego.

 2
Author: Mark Cidade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:13:20

Z punktu widzenia czytelności, to bardziej wyraźnie mówi przyszłych czytelników kodu " te rzeczy tutaj są ważne, to musi być zrobione bez względu na to, co się stanie."To jest dobre.

Również puste stwierdzenia mają tendencję do posiadania pewnego "zapachu" dla nich. Mogą one być znakiem, że deweloperzy nie zastanawiają się nad różnymi wyjątkami, które mogą wystąpić i jak sobie z nimi radzić.

 2
Author: Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:15

Finally jest opcjonalne -- nie ma powodu, aby mieć blok "Finally", jeśli nie ma środków do czyszczenia.

 2
Author: Guy Starbuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:39

Pochodzi z: tutaj

Podnoszenie i wyłapywanie WYJĄTKÓW nie powinno odbywać się rutynowo w ramach pomyślnego wykonania metody. Podczas tworzenia bibliotek klas, kod klienta musi mieć możliwość przetestowania pod kątem błędu przed wykonaniem operacji, która może spowodować wystąpienie wyjątku. Na przykład System. IO. FileStream udostępnia właściwość CanRead, którą można sprawdzić przed wywołaniem metody Read, zapobiegając potencjalnemu wyjątkowi podniesiony, jak pokazano w poniższym fragmencie kodu:

Dim str AS Stream = GetStream() If (str.CanRead) Następnie "kod do odczytu strumienia End If

Decyzja, czy sprawdzić stan obiektu przed wywołaniem konkretnej metody, która może wywołać wyjątek, zależy od oczekiwanego stanu obiektu. Jeśli obiekt FileStream jest tworzony przy użyciu ścieżki pliku, która powinna istnieć i konstruktora, który powinien zwrócić plik w trybie odczytu, sprawdzanie właściwości CanRead nie jest konieczne; niemożność odczytania strumienia plików byłaby naruszeniem oczekiwanego zachowania wywołań metody i należy zgłosić wyjątek. W przeciwieństwie do tego, jeśli metoda jest udokumentowana jako zwracająca referencję FileStream, która może lub nie może być czytelna, zaleca się sprawdzenie właściwości CanRead przed próbą odczytania danych.

Aby zilustrować wpływ wydajności, jaki może spowodować użycie techniki kodowania "run until exception", wydajność rzutu, który rzuca InvalidCastException jeśli cast się nie powiedzie, jest porównywany z operatorem C# as, który zwraca NULL jeśli cast się nie powiedzie. Wydajność obu technik jest identyczna w przypadku, gdy obsada jest ważna (zobacz Test 8.05), ale w przypadku, gdy obsada jest nieważna, a użycie obsady powoduje wyjątek, użycie obsady jest 600 razy wolniejsze niż użycie operatora as (zobacz Test 8.06). Efekt wysokiej wydajności techniki rzutu wyjątkowego obejmuje koszt przydzielania, rzucania i łapania wyjątek i koszt późniejszego usuwania śmieci obiektu wyjątku, co oznacza, że chwilowy wpływ wyrzucenia wyjątku nie jest tak wysoki. Ponieważ wyrzucanych jest więcej WYJĄTKÓW, częstym usuwaniem śmieci staje się problem, więc ogólny wpływ częstego używania techniki kodowania rzucania WYJĄTKÓW będzie podobny do testu 8.05.

 2
Author: SpoiledTechie.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:42

To zła praktyka, aby dodać klauzulę catch tylko po to, aby zmienić wyjątek.

 2
Author: Bastien Vandamme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-27 12:48:15

Użyj Try..Catch..Finally, jeśli twoja metoda wie, jak obsługiwać wyjątek lokalnie. Wyjątek występuje w Try, handed in Catch i po tym sprzątanie odbywa się w końcu.

W przypadku, gdy twoja metoda nie wie, jak obsłużyć wyjątek, ale wymaga oczyszczenia po jego wystąpieniu, użyj Try..Finally

Przez to wyjątek jest propagowany do metod wywołujących i obsługiwany, jeśli w metodach wywołujących znajdują się odpowiednie polecenia Catch.Jeżeli w bieżącej metodzie nie ma obsługi wyjątków lub którąkolwiek z metod wywołujących, a następnie aplikacja ulega awarii.

Przez Try..Finally jest zapewnione, że lokalne czyszczenie zostanie wykonane przed rozmnożeniem wyjątku od metod wywołujących.

 2
Author: manjuv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-20 05:56:32

Z finally, możesz wyczyścić zasoby, nawet jeśli Twoje polecenie catch wyrzuca wyjątek do programu wywołującego. Z twoim przykładem zawierającym pustą instrukcję catch, nie ma różnicy. Jeśli jednak w Twoim połowu, wykonasz pewne przetwarzanie i wyrzucisz błąd, lub nawet po prostu nie masz nawet połowu, w końcu nadal będzie działać.

 1
Author: Kibbee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:18

Po pierwsze, zła praktyka łapania WYJĄTKÓW, z którymi się nie radzisz. Sprawdź Rozdział 5 o wydajności. Netz Poprawa wydajności i skalowalności aplikacji.Net. Uwaga na marginesie, prawdopodobnie powinieneś ładować strumień wewnątrz bloku try, w ten sposób możesz złapać odpowiedni wyjątek, jeśli się nie powiedzie. Tworzenie strumienia poza blokiem try zaprzecza jego celowi.

 1
Author: Factor Mystic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:18:17

Jeśli przeczytasz C # dla programistów zrozumiesz, że ostatecznie blok został zaprojektowany tak, aby zoptymalizować aplikację i zapobiec wyciekowi pamięci.

CLR nie eliminuje całkowicie wycieków... wycieki pamięci mogą wystąpić, jeśli program nieumyślnie przechowuje odwołania do niechcianych obiektów]}

Na przykład, gdy otworzysz połączenie z plikiem lub bazą danych, twój komputer przydzieli pamięć do obsługi tej transakcji, a pamięć ta nie będzie przechowywana, chyba że usunięta albo polecenie close zostało wykonane. ale jeśli podczas transakcji wystąpił błąd, polecenie kontynuujące zostanie zakończone, chyba że znajdowało się wewnątrz bloku try.. finally...

catch różni się od finally w tym sensie, że catch został zaprojektowany, aby dać ci sposób na obsługę / zarządzanie lub interpretację błędu. Pomyśl o tym jak o osobie, która mówi: "Hej, złapałem kilku złych facetów, co mam im zrobić?" podczas gdy finally został zaprojektowany, aby upewnić się, że Twoje zasoby zostały prawidłowo umieszczone. Pomyśl o to kogoś, że niezależnie od tego, czy jest jakiś złoczyńca, upewni się, że Twoja nieruchomość była nadal bezpieczna.

I powinieneś pozwolić tym dwóm pracować razem na dobre.

Na przykład:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}
 1
Author: dr.Crow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-29 10:33:10

Wśród prawdopodobnie wielu powodów, wyjątki są bardzo powolne w wykonaniu. Możesz łatwo sparaliżować czas egzekucji, jeśli to się często zdarza.

 0
Author: lotsoffreetime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:41

Problem z blokami try / catch, które łapią wszystkie wyjątki polega na tym, że twój program jest teraz w nieokreślonym stanie, jeśli wystąpi nieznany wyjątek. Jest to całkowicie sprzeczne z zasadą Fail fast-nie chcesz, aby twój program był kontynuowany, jeśli wystąpi wyjątek. Powyższy try / catch może nawet wyłapać OutOfMemoryExceptions, ale na pewno jest to stan, w którym twój program nie będzie działał.

Try / finally bloki pozwalają na wykonanie czyszczenia kodu, a jednocześnie szybkie niepowodzenie. Dla większości okoliczności, chcesz tylko wyłapać wszystkie wyjątki na poziomie globalnym, aby móc je zalogować,a następnie wyjść.

 0
Author: David Mohundro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:14:48

Efektywna różnica między twoimi przykładami jest znikoma, dopóki nie zostaną rzucone żadne wyjątki.

Jeśli jednak w klauzuli 'try' zostanie wyrzucony wyjątek, pierwszy przykład pochłonie go całkowicie. Drugi przykład podniesie wyjątek do następnego kroku w górę stosu wywołań, więc różnica w podanych przykładach jest taka, że jeden całkowicie zaciemnia wszelkie wyjątki( pierwszy przykład), a drugi (drugi przykład) zachowuje informacje o wyjątkach dla potencjalnych później obsługi podczas wykonywania treści w klauzuli 'finally'.

Jeśli, na przykład, umieścisz kod w klauzuli 'catch' pierwszego przykładu, który wyrzucił wyjątek (albo ten, który został pierwotnie podniesiony, albo nowy), kod czyszczenia programu reader nigdy nie zostanie wykonany. Ostatecznie wykonuje niezależnie od tego, co dzieje się w klauzuli' catch'.

Więc główną różnicą między' catch 'I' finally 'jest to, że Zawartość bloku' finally '( z kilkoma rzadkimi wyjątki) mogą być uznane za gwarantowane do wykonania, nawet w obliczu nieoczekiwanego wyjątku, podczas gdy każdy kod po klauzuli ' catch '(ale poza klauzulą' finally') nie nosiłby takiej gwarancji.

Nawiasem mówiąc, Stream i StreamReader implementują IDisposable i mogą być owinięte w' using ' blok. Bloki' Using ' są semantycznym odpowiednikiem try / finally( no 'catch'), więc twój przykład może być bardziej zwięźle wyrażony jako:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

...które zamkną i pozbądź się instancji StreamReader, gdy wyjdzie poza zakres. Mam nadzieję, że to pomoże.

 0
Author: Jared,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 18:28:06

Try { ... } catch {} nie zawsze jest zły. Nie jest to powszechny wzorzec, ale używam go, gdy muszę zamknąć zasoby bez względu na wszystko, jak zamknięcie (ewentualnie) otwartych gniazd na końcu wątku.

 0
Author: Martin Liesén,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 22:26:20