Natknąłem się na tę stronę MSDN, która stwierdza:
Nie należy celowo zgłaszać wyjątków , wyjątków SystemException , NullReferenceException ani IndexOutOfRangeException z własnego kodu źródłowego.
Niestety nie zawraca sobie głowy wyjaśnieniem, dlaczego. Mogę odgadnąć powody, ale mam nadzieję, że ktoś bardziej autorytatywny w tej sprawie może udzielić ich wglądu.
Pierwsze dwa mają jakiś oczywisty sens, ale dwa ostatnie wydają się być tymi, które chciałbyś wykorzystać (a tak naprawdę mam).
Co więcej, czy to jedyne wyjątki, których należy unikać? Jeśli są inni, czym oni są i dlaczego ich również należy unikać?
c#
exception-handling
DonBoitnott
źródło
źródło
NullArgumentException
którego niektórzy ludzie mogą pomylić oba.ApplicationException
Odpowiedzi:
Exception
jest typem podstawowym dla wszystkich wyjątków i jako taki jest strasznie niespecyficzny. Nie powinieneś nigdy zgłaszać tego wyjątku, ponieważ po prostu nie zawiera on żadnych przydatnych informacji. Wywołanie kodu przechwytującego wyjątki nie może odróżnić celowo rzuconego wyjątku (z Twojej logiki) od innych wyjątków systemowych, które są całkowicie niepożądane i wskazują rzeczywiste błędy.Ten sam powód dotyczy również
SystemException
. Jeśli spojrzysz na listę typów pochodnych, zobaczysz ogromną liczbę innych wyjątków o bardzo różnej semantyce.NullReferenceException
iIndexOutOfRangeException
są innego rodzaju. Teraz są to bardzo specyficzne wyjątki, więc ich wyrzucenie może być w porządku. Jednak nadal nie będziesz chciał ich wyrzucać, ponieważ zwykle oznaczają one, że w twojej logice są pewne błędy. Na przykład wyjątek odwołania zerowego oznacza, że próbujesz uzyskać dostęp do elementu członkowskiego obiektu, którym jestnull
. Jeśli taka możliwość występuje w Twoim kodzie, zawsze powinieneś jawnie sprawdzićnull
i zamiast tego zgłosić bardziej przydatny wyjątek (na przykładArgumentNullException
). PodobnieIndexOutOfRangeException
s pojawiają się, gdy uzyskujesz dostęp do nieprawidłowego indeksu (na tablicach - nie na listach). Powinieneś zawsze się upewnić, że nie robisz tego w pierwszej kolejności i najpierw sprawdź granice np. Tablicy.Istnieje kilka innych wyjątków, takich jak te dwa, na przykład
InvalidCastException
lubDivideByZeroException
, które są generowane w przypadku określonych błędów w kodzie i zwykle oznaczają, że robisz coś źle lub nie sprawdzasz najpierw niektórych nieprawidłowych wartości. Wyrzucając je świadomie z kodu, utrudniasz po prostu kodowi wywołującemu określenie, czy zostały one wyrzucone z powodu błędu w kodzie, czy po prostu dlatego, że zdecydowałeś się użyć ich ponownie w implementacji.Oczywiście są pewne wyjątki (hah) od tych zasad. Jeśli tworzysz coś, co może powodować wyjątek, który dokładnie pasuje do istniejącego, możesz z tego skorzystać, zwłaszcza jeśli próbujesz dopasować jakieś wbudowane zachowanie. Po prostu upewnij się, że wybrałeś bardzo konkretny typ wyjątku.
Ogólnie rzecz biorąc, jeśli nie znajdziesz (konkretnego) wyjątku, który spełnia Twoje wymagania, zawsze powinieneś rozważyć utworzenie własnych typów wyjątków dla określonych oczekiwanych wyjątków. Szczególnie podczas pisania kodu biblioteki może to być bardzo przydatne do oddzielenia źródeł wyjątków.
źródło
IList
implementację, nie jesteś w stanie wpłynąć na żądane indeksy, jest to błąd logiczny dzwoniącego, gdy indeks jest nieprawidłowy i możesz go tylko poinformować tego błędu logicznego, zgłaszając odpowiedni wyjątek. DlaczegoIndexOutOfRangeException
nie jest właściwe?IList
, będziesz rzucać,ArgumentOutOfRangeException
jak sugeruje dokumentacja interfejsu .IndexOutOfRangeException
dotyczy tablic iz tego, co wiem, nie można ponownie implementować tablic.NullReferenceException
jest zwykle zgłaszane wewnętrznie jako specjalny przypadekAccessViolationException
(IIRC test jest czymś w rodzajucmp [addr], addr
, tj. Próbuje wyłuskać wskaźnik i jeśli nie powiedzie się z naruszeniem dostępu, obsługuje różnicę między NRE i AVE w wynikowym programie obsługi przerwań). Więc oprócz powodów semantycznych, w grę wchodzi również oszustwo. Może to również zniechęcić Cię donull
ręcznego sprawdzania, czy nie jest to pomocne - jeśli i tak zamierzasz rzucić NRE, dlaczego nie pozwolić, aby .NET to zrobił?Podejrzewam, że celem dwóch ostatnich jest zapobieżenie pomyłkom z wbudowanymi wyjątkami, które mają oczekiwane znaczenie. Uważam jednak, że jeśli zachowujesz dokładny cel wyjątku : to jest właściwy
throw
. Na przykład, jeśli piszesz zbiór niestandardowy, użycie go wydaje się całkowicie uzasadnioneIndexOutOfRangeException
- jaśniejsze i bardziej szczegółowe, IMO niżArgumentOutOfRangeException
. I chociażList<T>
można wybrać to drugie, istnieje co najmniej 41 miejsc (dzięki uprzejmości reflektora) w BCL (bez tablic), które rzucają na zamówienieIndexOutOfRangeException
- z których żadne nie jest na tyle „niskiego poziomu”, by zasługiwać na specjalne zwolnienie. Więc tak, myślę, że możesz słusznie argumentować, że ta wskazówka jest głupia. Również,NullReferenceException
jest trochę przydatne w metodach rozszerzających - jeśli chcesz zachować semantykę, że:rzuca
NullReferenceException
kiedyobj
jestnull
.źródło
SomeMethod()
nie ma potrzeby uzyskiwania dostępu do członków, wymuszanie tego jest nieprawidłowe. Podobnie: podnieś ten punkt z 41 miejscami w BCL, które tworzą niestandardoweIndexOutOfRangeException
, i 16 miejscami, które tworzą niestandardoweNullReferenceException
ArgumentNullException
zamiast aNullReferenceException
. Nawet jeśli cukier składniowy z metod rozszerzających zezwala na tę samą składnię, co zwykły dostęp do elementu członkowskiego, nadal działa bardzo różnie. Uzyskanie NRE zMyStaticHelpers.SomeMethod(obj)
byłoby po prostu złe.Jak wskazałeś, w artykule Tworzenie i zgłaszanie wyjątków (przewodnik programowania w języku C #) w temacie Rzeczy, których należy unikać podczas zgłaszania wyjątków , firma Microsoft rzeczywiście podaje
System.IndexOutOfRangeException
jako typ wyjątku, który nie powinien być celowo wyrzucany z własnego kodu źródłowego.W przeciwieństwie jednak do tego w artykule (odwołanie do języka C #) Microsoft wydaje się naruszać własne wytyczne. Oto metoda, którą Microsoft zawarł w swoim przykładzie:
Tak więc sam Microsoft nie jest konsekwentny, ponieważ demonstruje wyrzucanie
IndexOutOfRangeException
w swojej dokumentacji dlathrow
!To prowadzi mnie do przekonania, że przynajmniej w przypadku
IndexOutOfRangeException
, mogą zaistnieć sytuacje, w których ten typ wyjątku może zostać rzucony przez programistę i uznany za dopuszczalną praktykę.źródło
Kiedy przeczytałem twoje pytanie, zadałem sobie pytanie, w jakich warunkach chciałoby się wyrzucić typy wyjątków
NullReferenceException
,InvalidCastException
lubArgumentOutOfRangeException
.Moim zdaniem, kiedy napotykam jeden z tych typów wyjątków, ja (programista) czuję się zaniepokojony ostrzeżeniem w tym sensie, że kompilator do mnie mówi. Tak więc, zezwolenie Tobie (programiście) na rzucanie takich typów wyjątków jest równoważne (kompilatorowi) sprzedanie odpowiedzialności. Na przykład sugeruje to, że kompilator powinien teraz pozwolić programiście zdecydować, czy obiekt jest
null
. Ale podjęcie takiej decyzji powinno być naprawdę zadaniem kompilatora.PS: Od 2003 roku opracowuję własne wyjątki, więc mogę je rzucać, jak chcę. Myślę, że takie postępowanie jest uważane za najlepszą praktykę.
źródło
Odkładając dyskusję na temat
NullReferenceException
iIndexOutOfBoundsException
odkładając na bok:A co z łapaniem i rzucaniem
System.Exception
. Wrzuciłem ten typ wyjątku do swojego kodu i nigdy mnie to nie spieprzyło. Podobnie, bardzo często łapię niespecyficznyException
typ i też mi się to udało. Więc dlaczego tak jest?Zwykle użytkownicy twierdzą, że powinni umieć rozróżniać przyczyny błędów. Z mojego doświadczenia wynika, że jest tylko kilka sytuacji, w których chciałbyś inaczej obsługiwać różne typy wyjątków. W tych przypadkach, w których oczekujesz, że użytkownicy będą obsługiwać błędy programowo, należy zgłosić bardziej szczegółowy typ wyjątku. W innych przypadkach ogólne wytyczne dotyczące najlepszych praktyk nie przekonują mnie.
Więc jeśli chodzi o rzucanie
Exception
, nie widzę powodu, aby tego zabronić we wszystkich przypadkach.EDYCJA: również ze strony MSDN:
Nadużywanie klauzul catch z indywidualną logiką dla różnych typów wyjątków również nie jest najlepszą praktyką.
źródło