Jestem prawie pewien, że odpowiedź brzmi TAK. Jeśli używam bloku Try Final, ale nie używam bloku Catch, to wszelkie wyjątki BĘDĄ bąbelkowe. Poprawny?
Jakieś ogólne przemyślenia na temat praktyki?
Seth
Jestem prawie pewien, że odpowiedź brzmi TAK. Jeśli używam bloku Try Final, ale nie używam bloku Catch, to wszelkie wyjątki BĘDĄ bąbelkowe. Poprawny?
Jakieś ogólne przemyślenia na temat praktyki?
Seth
Odpowiedzi:
Tak, absolutnie tak. Zakładając finally
oczywiście, że twój blok nie zgłasza wyjątku, w takim przypadku skutecznie "zastąpi" ten, który został pierwotnie rzucony.
Jakieś ogólne przemyślenia na temat praktyki?
Tak. Ostrożnie . Kiedy twój ostatni blok jest uruchomiony, jest całkowicie możliwe, że działa, ponieważ został zgłoszony nieobsłużony, nieoczekiwany wyjątek . Oznacza to, że coś jest zepsute i może się wydarzyć coś zupełnie nieoczekiwanego .
W takiej sytuacji prawdopodobnie w ogóle nie powinieneś uruchamiać kodu w końcowych blokach. Kod w bloku końcowym można zbudować tak, aby zakładał, że podsystemy, od których zależy, są w dobrym stanie, podczas gdy w rzeczywistości mogą zostać głęboko uszkodzone. Kod w ostatnim bloku może pogorszyć sytuację.
Na przykład często widzę takie rzeczy:
DisableAccessToTheResource();
try
{
DoSomethingToTheResource();
}
finally
{
EnableAccessToTheResource();
}
Autor tego kodu myśli: „Dokonuję tymczasowej mutacji stanu świata; muszę przywrócić stan, jaki był przed wezwaniem”. Ale zastanówmy się, jak może to się nie udać.
Po pierwsze, wywołujący mógł już zablokować dostęp do zasobu; w takim przypadku ten kod włącza go ponownie, prawdopodobnie przedwcześnie.
Po drugie, jeśli DoSomethingToTheResource zgłasza wyjątek, czy jest właściwe, aby umożliwić dostęp do zasobu ??? Kod zarządzający zasobem jest nieoczekiwanie uszkodzony . Ten kod mówi, że w efekcie „jeśli kod zarządzający jest uszkodzony, upewnij się, że inny kod może wywołać ten uszkodzony kod tak szybko, jak to możliwe, tak aby również mógł się strasznie zawieść ”. Wydaje się, że to zły pomysł.
Po trzecie, jeśli DoSomethingToTheResource zgłasza wyjątek, to skąd wiemy, że EnableAccessToTheResource również nie zgłosi wyjątku? Jakiekolwiek okropności, jakie spotkały użycie zasobu, mogą również wpłynąć na kod porządkujący, w którym to przypadku oryginalny wyjątek zostanie utracony, a problem będzie trudniejszy do zdiagnozowania.
Zwykle piszę taki kod bez używania bloków try-last:
bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
EnableAccessToTheResource();
Teraz stan nie jest zmutowany, chyba że musi. Teraz stan dzwoniącego nie jest zmieniany. A teraz, jeśli DoSomethingToTheResource zawiedzie, nie włączamy ponownie dostępu. Zakładamy, że coś jest głęboko zepsute i nie ryzykujemy pogorszenia sytuacji, próbując utrzymać działający kod. Jeśli to możliwe, pozwól dzwoniącemu rozwiązać problem.
Kiedy więc warto uruchomić ostatecznie blok? Po pierwsze, kiedy oczekuje się wyjątku. Na przykład można się spodziewać, że próba zablokowania pliku może się nie powieść, ponieważ ktoś inny go zablokował. W takim przypadku sensowne jest przechwycenie wyjątku i zgłoszenie go użytkownikowi. W takim przypadku niepewność co do tego, co jest zepsute, jest zmniejszona; raczej nie pogorszysz sytuacji, posprzątając.
Po drugie, gdy oczyszczany zasób jest rzadkim zasobem systemowym. Na przykład sensowne jest zamknięcie uchwytu pliku w końcowym bloku. („Używanie” to oczywiście tylko inny sposób zapisania bloku try-Final.) Zawartość pliku może być uszkodzona, ale nie możesz teraz nic na to poradzić. Uchwyt pliku zostanie ostatecznie zamknięty, więc równie dobrze może to nastąpić raczej wcześniej niż później.