Myślałem o tym i nie mogłem podać żadnego przykładu. Dlaczego ktoś miałby chcieć złapać wyjątek i nic z tym nie zrobić? Czy możesz podać przykład? Może jest to po prostu coś, czego nigdy nie należy robić.
Myślałem o tym i nie mogłem podać żadnego przykładu. Dlaczego ktoś miałby chcieć złapać wyjątek i nic z tym nie zrobić? Czy możesz podać przykład? Może jest to po prostu coś, czego nigdy nie należy robić.
Odpowiedzi:
Cały czas robię to z błędami konwersji w D:
import std.conv, std.stdio, std.exception;
void main(string[] args) {
enforce(args.length > 1, "Usage: foo.exe filename");
double[] nums;
// Process a text file with one number per line into an array of doubles,
// ignoring any malformed lines.
foreach(line; File(args[1]).byLine()) {
try {
nums ~= to!double(line);
} catch(ConvError) {
// Ignore malformed lines.
}
}
// Do stuff with nums.
}
To powiedziawszy, myślę, że wszystkie bloki catch powinny mieć coś w sobie, nawet jeśli to jest tylko komentarz wyjaśniający, dlaczego ignorujesz wyjątek.
Edycja: Chcę również podkreślić, że jeśli zamierzasz zrobić coś takiego, powinieneś być ostrożny, aby złapać tylko określony wyjątek, który chcesz zignorować. Robienie zwykłego starego catch {}
jest prawie zawsze złą rzeczą.
Jednym z przykładów, w którym moim zdaniem jest OK połknięcie wyjątku bez robienia czegokolwiek, nawet rejestrowanie wyjątku, znajduje się w samym kodzie rejestrowania.
Jeśli próbujesz coś zalogować i otrzymujesz wyjątek, niewiele możesz z tym zrobić:
Pozostawia to opcję „najmniejszego zła”, polegającą na cichym połykaniu, co pozwala aplikacji na wykonywanie głównej pracy, ale ogranicza możliwość jej rozwiązania.
Jeśli wyjątek zostanie zgłoszony przez operację, która i tak była opcjonalna, może nie być nic do zrobienia. Może się to nie przydać. W takim przypadku możesz po prostu chcieć go złapać i nic nie robić.
Jeśli to robisz, dołącz komentarz. Zawsze komentuj wszystko, co wygląda jak błąd (przegląd switch
instrukcji, pusty catch
blok, przypisanie w stanie).
Możesz argumentować, że nie jest to właściwe wykorzystanie wyjątków, ale to dotyczy innego pytania.
Puste catch
bloki są zapachem kodu w większości języków. Główną ideą jest stosowanie wyjątków w wyjątkowych sytuacjach i nie wykorzystywanie ich do kontroli logicznej. Wszystkie wyjątki muszą być gdzieś obsługiwane.
W wyniku zastosowania tego podejścia podczas programowania jednej konkretnej warstwy aplikacji masz kilka możliwości:
To tak naprawdę nie pozostawia miejsca na nic nie rób, puste catch
bloki.
ZMIENIONO DO DODANIA : załóżmy, że programujesz w języku, w którym zgłaszanie wyjątków jest normalnym sposobem kontrolowania logiki programu (jedna z alternatyw dla goto
). Wówczas pusty catch
blok w programie napisanym w tym języku jest bardzo podobny do pustego else
bloku w tradycyjnym języku (lub w ogóle go nie ma else
). Uważam jednak, że nie jest to styl programowania zalecany przez społeczności programistów C #, Java lub C ++.
W niektórych przypadkach Java wymaga obsługi wyjątku, który w żadnym wypadku nie może się zdarzyć. Rozważ ten kod:
try {
bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}
Każda implementacja platformy Java jest wymagana do obsługi następujących standardowych zestawów znaków.
...
UTF-8
Bez zepsucia platformy Java po prostu nie ma sposobu, aby spowodować ten wyjątek. Więc po co męczyć się z tym? Ale nie można po prostu pominąć klauzuli try-catch.
throw new Error(e)
aby mieć absolutną pewność, że niczego nie przegapiłem (lub nie zgłosiłem faktycznie uszkodzonej platformy).
Zasadniczo nie. Możesz albo rzucić wyjątek na stos, albo zarejestrować, co się stało.
Jednak często to robię, gdy analizuję ciągi znaków i konwertuję na liczby (szczególnie w przypadku mapowania projektów, które są użyteczne raz i odrzucają różnorodność).
boolean isNonZeroNumber = false;
try {
if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
b = true;
}
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}
return b;
W niektórych przypadkach wyjątek wcale nie jest wyjątkowy; w rzeczywistości jest to oczekiwane. Rozważ ten przykład:
/* Check if the process is still alive; exitValue() throws an exception if it is */
try {
p.exitValue();
processPool.remove(p);
}
catch (IllegalThreadStateException e) { /* expected behaviour */ }
Ponieważ w java.lang.Process () nie ma metody isAlive (), jedynym sposobem sprawdzenia, czy wciąż żyje, jest wywołanie metody exitValue (), która zgłasza wyjątek IllegalThreadStateException, jeśli proces nadal działa.
Według mojego skromnego doświadczenia rzadko jest to dobra rzecz. Twój przebieg może się jednak różnić.
Niemal za każdym razem, gdy widziałem to w naszej bazie kodu, dzieje się tak, ponieważ wyśledziłem błąd, który ukrył zjedzony wyjątek. Innymi słowy, nie podając żadnego kodu, aplikacja nie może wskazać błędu ani wykonać innej akcji.
Czasami, na przykład, w warstwach API możesz nie chcieć lub nie możesz pominąć wyjątków, więc w takim przypadku staram się wychwycić najbardziej ogólne, a następnie je zapisać. Jeszcze raz, aby przynajmniej dać szansę sesji debugowania / diagnozy.
Zazwyczaj, jeśli musi być pusty, przynajmniej robię to, twierdzę, że coś się dzieje podczas sesji debugowania. To powiedziawszy, jeśli nie mogę sobie z tym poradzić, zwykle całkowicie je pomijam.
IMO jest jedno miejsce, w którym puste wyciągi połowowe są OK. W przypadkach testowych, w których spodziewany jest wyjątek:
try
{
DoSomething(); //This should throw exception if everything works well
Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
//Expected to get here
}
Uważam, że istnieją pewne przypadki, w których uzasadnione jest posiadanie pustej klauzuli catch - są to przypadki, gdy chcesz, aby kod działał dalej, jeśli dana operacja się nie powiedzie. Myślę jednak, że ten wzór można łatwo nadużyć, dlatego każde użycie powinno być dokładnie zbadane, aby upewnić się, że nie ma lepszego sposobu na poradzenie sobie z nim. W szczególności nie należy tego nigdy robić, jeśli pozostawia on program w niespójnym stanie lub jeśli może to doprowadzić do awarii innej części kodu.
Nie wierzę w brak pewnego poziomu czujności w przypadku wystąpienia wyjątku - czy jednym z celów programowania nie powinno być ulepszanie kodu? Jeśli wyjątek wystąpi w 10% przypadków i nigdy się o nim nie dowiesz, wówczas wyjątek będzie zawsze taki zły lub gorszy.
Widzę jednak drugą stronę, że jeśli wyjątek nie powoduje rzeczywistej szkody w przetwarzaniu kodu, to po co narażać użytkownika na to. Rozwiązaniem, które zazwyczaj wdrażam, jest rejestrowanie go przez moją klasę loggera (czyli w każdej klasie, jaką kiedykolwiek piszę w pracy).
Jeśli jest to łagodny wyjątek, wywołuję metodę .Debug () mojego Loggera; w przeciwnym razie Logger.Error () (i (być może) rzut).
try
{
doWork();
}
catch(BenignException x)
{
_log.Debug("Error doing work: " + x.Message);
}
Nawiasem mówiąc, w mojej implementacji mojego programu rejestrującego wywołanie metody .Debug () spowoduje zarejestrowanie go tylko wtedy, gdy mój plik App.Config określi, że poziom dziennika wykonania programu jest w trybie debugowania.
Sprawdzanie istnienia jest dobrym przykładem użycia:
// If an exception happens, it doesn't matter. Log the initial value or the new value
function logId(person) {
let id = 'No ID';
try {
id = person.data.id;
} catch {}
console.log(id);
}
Innym przypadkiem finally
jest użycie klauzuli:
function getter(obj){}
function objChecker()
{
try
{
/* Check argument length and constructor type */
if (getter.length === 1 && getter.constructor === Function)
{
console.log("Correct implementation");
return true;
}
else
{
console.log("Wrong implementation");
return false;
}
}
catch(e)
{
}
finally
{
console.log(JSON.stringify(getter))
}
}
// Test the override of the getter method
getter = getter;
objChecker();
getter = [];
objChecker();
getter = {};
objChecker();
getter = RegExp;
objChecker();
getter = Function;
objChecker();
Proponuje się zezwolenie na pominięcie wiązania połowów w ECMAScript, które dotyczy tego i podobnych przypadków użycia.
Bibliografia
Jedyny raz, kiedy naprawdę potrzebowałem zrobić coś takiego, to klasa logowania dla aplikacji konsolowej. Istnieje globalny moduł obsługi catch najwyższego poziomu, który wysłał błąd do STDOUT, zarejestrował błąd do pliku, a następnie zamknął aplikację kodem błędu> 0.
Problem polegał na tym, że czasami logowanie do pliku kończyło się niepowodzeniem. Uprawnienia do katalogu powstrzymały cię przed zapisaniem pliku, plik został zablokowany, cokolwiek. Jeśli tak się stanie, nie będę w stanie obsłużyć wyjątku w taki sam sposób, jak gdzie indziej (złap, zarejestruj odpowiednie dane , zawiń lepszy typ wyjątku itp.), Ponieważ zarejestrowanie szczegółów wyjątku spowodowało, że program rejestrujący wykonał te same działania ( próba zapisu do pliku), który już się nie powiódł. Kiedy znów zawiodły ... Widzisz, dokąd idę. Wpada w nieskończoną pętlę.
Jeśli upuścisz niektóre bloki try {}, możesz skończyć z wyjątkiem pojawiającym się w oknie komunikatu (nie to, czego potrzebujesz dla aplikacji do cichej konfiguracji). Więc w tej metodzie, która zapisuje do pliku dziennika, mam pustą procedurę obsługi catch. Nie powoduje to zakopania błędu, ponieważ nadal pojawia się kod błędu> 0, po prostu zatrzymuje nieskończoną pętlę i pozwala aplikacji z wdziękiem wyjść.
Jest oczywiście komentarz wyjaśniający, dlaczego jest pusty.
(Zamierzałem to opublikować wcześniej, po prostu bałam się, że stanę w płomieniach zapomnienia. Ponieważ nie jestem jedyny ...)
Jest to odpowiednie w sytuacji, gdy trzeba wykonać pewną sekwencję bez względu na to, co ma najbardziej krytyczny element na końcu.
Na przykład. W kontroli ruchu komunikacja hosta ze sterownikiem. W sytuacjach awaryjnych istnieje szereg kroków przygotowujących do przerwania ruchu, zatrzymania generowania trajektorii, spowolnienia silników, umożliwienia przerw, wyłączenia wzmacniacza, a następnie należy zabić moc magistrali. Wszystko musi się zdarzyć sekwencyjnie, a jeśli jeden z kroków się nie powiedzie, pozostałe kroki muszą zostać wykonane mimo to, nawet przy akceptowanym ryzyku uszkodzenia sprzętu. Powiedz, że nawet jeśli wszystkie powyższe zawiodą, moc magistrali zabijania musi się zdarzyć.
Z wdzięcznym zamknięciem zasobów systemowych w celu odzyskania po błędzie
Na przykład. Jeśli używasz usługi w tle, które nie dostarcza informacji zwrotnej do użytkownika, może nie chcieć, aby popchnąć wskazanie błędu dla użytkownika, ale nie trzeba zamykać się zasoby są wykorzystywane w sposób wdzięku.
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
Najlepiej byłoby użyć funkcji catch w trybie debugowania, aby wykryć błędy i wydrukować je w konsoli lub w pliku dziennika w celu przetestowania. W środowisku produkcyjnym usługi w tle zasadniczo nie powinny przeszkadzać użytkownikowi, gdy zostanie zgłoszony błąd, więc wyjście błędu powinno zostać stłumione.