Będzie to bardziej konceptualna odpowiedź na pytanie, dlaczego te ostrzeżenia mogą istnieć nawet przy podejściu typu „spróbuj z zasobami”. Niestety nie jest to łatwe rozwiązanie, na które możesz liczyć.
Odzyskiwanie po błędzie nie może zakończyć się niepowodzeniem
finally
modeluje przepływ kontrolny po transakcji, który jest wykonywany niezależnie od tego, czy transakcja się powiedzie, czy nie.
W przypadku fail, finally
przechwytuje logiczne, że jest wykonywany w środku odzyskania z błędu, zanim został on w pełni sprawny (zanim osiągniemy nasz catch
cel).
Wyobraźmy sobie, że problemy koncepcyjne prezentuje do wystąpienia błędu w środku odzyskania z błędu.
Wyobraź sobie serwer bazy danych, w którym próbujemy dokonać transakcji, a transakcja kończy się niepowodzeniem (powiedzmy, że serwerowi zabrakło pamięci w środku). Teraz serwer chce wycofać transakcję do punktu, tak jakby nic się nie stało. Wyobraź sobie jednak, że napotyka kolejny błąd w procesie wycofywania. Teraz mamy do czynienia z transakcją w połowie zatwierdzoną w bazie danych - atomowość i niepodzielny charakter transakcji jest teraz zepsuty, a integralność bazy danych zostanie teraz zagrożona.
Ten problem koncepcyjny występuje w każdym języku, który zajmuje się błędami, niezależnie od tego, czy jest to C z ręczną propagacją kodu błędu, C ++ z wyjątkami i destruktorami, czy Java z wyjątkami i finally
.
finally
nie może zawieść w językach, które zapewniają to w ten sam sposób, w jaki niszczyciele nie mogą zawieść w C ++ podczas napotkania wyjątków.
Jedynym sposobem na uniknięcie tego koncepcyjnego i trudnego problemu jest upewnienie się, że proces wycofywania transakcji i zwalniania zasobów w środku nie może napotkać rekurencyjnego wyjątku / błędu.
Zatem jedynym bezpiecznym projektem jest tutaj projekt, w którym writer.close()
nie może zawieść. Zazwyczaj istnieją sposoby, aby uniknąć scenariuszy, w których takie rzeczy mogą zawieść w trakcie odzyskiwania, co uniemożliwia.
Jest to niestety jedyny sposób - odzyskiwanie po błędzie nie może zakończyć się niepowodzeniem. Najprostszym sposobem, aby to zapewnić, jest uniemożliwienie tego rodzaju „uwolnienia zasobów” i „odwrotnych efektów ubocznych”. To nie jest łatwe - prawidłowe odzyskiwanie błędów jest trudne, a także niestety trudne do przetestowania. Ale sposobem na osiągnięcie tego jest upewnienie się, że wszelkie funkcje, które „niszczą”, „zamykają”, „zamykają”, „wycofują” itp., Nie mogą napotkać zewnętrznego błędu w procesie, ponieważ takie funkcje często będą musiały zostać wywołanym w trakcie odzyskiwania po istniejącym błędzie.
Przykład: rejestrowanie
Powiedzmy, że chcesz zalogować rzeczy w finally
bloku. Jest to często poważny problem, chyba że rejestracja nie powiedzie się . Logowanie prawie na pewno może się nie powieść, ponieważ może chcieć dołączyć więcej danych do pliku, a to może łatwo znaleźć wiele przyczyn niepowodzenia.
Rozwiązaniem jest tutaj, aby żadna funkcja rejestrowania używana w finally
blokach nie mogła przekazać obiektu wywołującego (może się nie powieść, ale nie wyrzuci). Jak możemy to zrobić? Jeśli twój język pozwala na rzucanie w kontekście pod warunkiem, że istnieje zagnieżdżony blok try / catch, byłby to jeden ze sposobów uniknięcia rzucania dzwoniącemu przez połykanie wyjątków i zamienianie ich w kody błędów, np. Może rejestrowanie może odbywać się w osobnym proces lub wątek, które mogą zawieść osobno i poza istniejącym stosem odzyskiwania po awarii, rozwijają się. Tak długo, jak możesz komunikować się z tym procesem bez możliwości wystąpienia błędu, byłoby to również bezpieczne, ponieważ problem bezpieczeństwa występuje tylko w tym scenariuszu, jeśli rekurencyjnie rzucamy z tego samego wątku.
W takim przypadku możemy uniknąć awarii rejestrowania, pod warunkiem, że nie rzuca się, ponieważ nie można się zalogować i nic nie robić, to nie koniec świata (nie wycieknie żadnych zasobów lub nie przywróci efektów ubocznych, np.).
W każdym razie jestem pewien, że możesz już zacząć wyobrażać sobie, jak niesamowicie trudno jest naprawdę uczynić oprogramowanie bezpiecznym dla wyjątków. Być może nie będzie konieczne uzyskanie tego w jak największym stopniu we wszystkich programach oprócz najbardziej krytycznych dla misji. Warto jednak zwrócić uwagę na to, jak naprawdę osiągnąć bezpieczeństwo wyjątków, ponieważ nawet autorzy bibliotek o bardzo ogólnym przeznaczeniu często grzebią tutaj i niszczą całe bezpieczeństwo wyjątków aplikacji korzystającej z biblioteki.
SomeFileWriter
Jeśli SomeFileWriter
można wrzucić do środka close
, powiedziałbym, że jest to generalnie niezgodne z obsługą wyjątków, chyba że nigdy nie spróbujesz go zamknąć w kontekście wymagającym odzyskania z istniejącego wyjątku. Jeśli kod jest poza twoją kontrolą, możemy być SOL, ale warto powiadomić autorów tego rażącego problemu dotyczącego bezpieczeństwa wyjątków. Jeśli jest to twoja kontrola, moim głównym zaleceniem jest upewnienie się, że zamknięcie go nie może zakończyć się niepowodzeniem za pomocą wszelkich niezbędnych środków.
Wyobraź sobie, że system operacyjny może nie zamknąć pliku. Teraz każdy program, który próbuje zamknąć plik podczas zamykania, nie zostanie zamknięty . Co powinniśmy teraz zrobić, po prostu utrzymuj aplikację otwartą i zawieszoną (prawdopodobnie nie), po prostu wyciek zasobu pliku i zignoruj problem (może w porządku, jeśli nie jest to takie krytyczne)? Najbezpieczniejszy projekt: spraw, aby zamknięcie pliku było niemożliwe.