Dlaczego nie mamy rzucać tych wyjątków?

Natknąłem się na tę stronę MSDN , która stwierdza:

Nie rzucaj wyjątek, SystemException, NullReferenceException , lub IndexOutOfRangeException celowo z własnego kodu źródłowego.

Niestety, nie trzeba tłumaczyć dlaczego. Mogę odgadnąć powody, ale mam nadzieję, że ktoś bardziej autorytatywny w tym temacie może zaoferować swój wgląd.

Pierwsze dwa mają jakiś oczywisty sens, ale drugie dwa wydają się takie, które chciałbyś zatrudnić(a w rzeczywistości mam).

Dalej, czy są to jedyne wyjątki, których należy unikać? Jeśli są inni, to czym są i dlaczego należy ich unikać?

Author: poke, 2014-03-17

4 answers

Exception jest typem bazowym dla wszystkich wyjątków i jako taki strasznie niespecyficzny. Nigdy nie należy wyrzucać tego wyjątku, ponieważ po prostu nie zawiera żadnych użytecznych informacji. Wywołanie przechwytywania kodu dla WYJĄTKÓW nie mogło odróżnić celowo wyrzuconego wyjątku (z Twojej logiki) od innych wyjątków systemowych, które są całkowicie niepożądane i wskazują na prawdziwe błędy.

Ten sam powód dotyczy również SystemException. Jeśli spojrzysz na listę typów pochodnych, możesz zobaczyć ogromną liczbę innych wyjątków o bardzo różnej semantyce.

NullReferenceException oraz IndexOutOfRangeException są innego rodzaju. Teraz są to bardzo specyficzne wyjątki, więc rzucanie nimi Może być w porządku. Jednak nadal nie będziesz chciał ich rzucać, ponieważ zazwyczaj oznaczają one, że w Twojej logice są pewne rzeczywiste błędy. Na przykład wyjątek null reference oznacza, że próbujesz uzyskać dostęp do elementu obiektu, który jest null. Jeśli jest taka możliwość w Twój kod, wtedy powinieneś zawsze wyraźnie sprawdzić null i zamiast tego rzucić bardziej użyteczny wyjątek (na przykład ArgumentNullException). Podobnie, IndexOutOfRangeException s występują, gdy uzyskujesz dostęp do nieprawidłowego indeksu (na tablicach-nie listach). Należy zawsze upewnić się, że nie robisz tego w pierwszej kolejności i najpierw sprawdzić granice np. tablicy.

Istnieje kilka innych wyjątków, takich jak te dwa, na przykład InvalidCastException lub DivideByZeroException, które są wyrzucane za określone błędy w kodzie zazwyczaj oznacza to, że robisz coś źle lub nie sprawdzasz najpierw nieprawidłowych wartości. Rzucając je świadomie z kodu, utrudniasz kodowi wywołującemu ustalenie, czy zostały wyrzucone z powodu jakiejś usterki w kodzie, czy tylko dlatego, że zdecydowałeś się użyć ich ponownie do czegoś w swojej implementacji.

Oczywiście istnieją pewne wyjątki (hah) od tych zasad. Jeśli budujesz coś, co może spowodować wyjątek, który dokładnie pasuje istniejące, a następnie można go używać, zwłaszcza jeśli próbujesz dopasować niektóre wbudowane zachowania. Upewnij się tylko, że wybrałeś bardzo konkretny typ wyjątku.

Ogólnie rzecz biorąc, jeśli nie znajdziesz (konkretnego) wyjątku, który spełni Twoje potrzeby, zawsze powinieneś rozważyć utworzenie własnych typów wyjątków dla konkretnych oczekiwanych WYJĄTKÓW. Szczególnie, gdy piszesz kod biblioteki, może to być bardzo przydatne do oddzielenia źródeł WYJĄTKÓW.

 80
Author: poke,
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
2014-03-31 11:37:32

Podejrzewam, że intencją ostatnich 2 jest uniknięcie pomyłek z wbudowanymi wyjątkami, które mają oczekiwane znaczenie. Jednak jestem zdania, że jeśli zachowujesz dokładną intencję wyjątku : jest to poprawne do throw. Na przykład, jeśli piszesz kolekcję niestandardową, wydaje się całkowicie rozsądne użycie IndexOutOfRangeException - jaśniejsze i bardziej szczegółowe, IMO, niż ArgumentOutOfRangeException. I chociaż List<T> może wybrać to drugie, jest co najmniej 41 miejsc (dzięki uprzejmości reflector) w BCL (nie wliczając tablic), które rzucają IndexOutOfRangeException- żadna z nich nie jest na tyle "niski poziom", aby zasługiwać na specjalne wyłączenie. Więc tak, myślę, że można słusznie argumentować, że wytyczne są głupie. Podobnie, NullReferenceException jest trochę przydatny w metodach rozszerzeń - jeśli chcesz zachować semantyczny, który:

obj.SomeMethod(); // this is actually an extension method

Rzuca NullReferenceException Gdy obj jest null.

 33
Author: Marc Gravell,
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
2014-03-17 12:03:08

Jak zauważyłeś, w artykule Tworzenie i wyrzucanie wyjątków (C# Programming Guide) w temacie rzeczy, których należy unikać podczas wyrzucania WYJĄTKÓW, Microsoft rzeczywiście wymienia System.IndexOutOfRangeException jako typ wyjątków, które nie powinny być celowo wyrzucane z twojego własnego kodu źródłowego.

W przeciwieństwie do tego, w artykule throw (C# Reference) Microsoft wydaje się naruszać własne wytyczne. Oto metoda, którą Microsoft zawarł w swoim przykład:
static int GetNumber(int index)
{
    int[] nums = { 300, 600, 900 };
    if (index > nums.Length)
    {
        throw new IndexOutOfRangeException();
    }
    return nums[index];
}

Więc sam Microsoft nie jest konsekwentny, ponieważ demonstruje rzucanie IndexOutOfRangeException w swojej dokumentacji dla throw!

To prowadzi mnie do przekonania, że przynajmniej w przypadku IndexOutOfRangeException, mogą być sytuacje, w których ten typ wyjątku can być rzucane przez programistę i być uważane za akceptowalną praktykę.

 3
Author: DavidRR,
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
2014-04-25 18:12:15

Kiedy przeczytałem twoje pytanie, zadałem sobie pytanie, na jakich warunkach chce się rzucać typy WYJĄTKÓW NullReferenceException, InvalidCastException lub ArgumentOutOfRangeException.

Moim zdaniem, napotykając jeden z tych wyjątków, ja (deweloper) odczuwam zaniepokojenie ostrzeżeniem w tym sensie, że kompilator do mnie mówi. Tak więc pozwolenie Ci (deweloperowi) na rzucanie takich typów wyjątków jest równoważne (kompilatorowi) sprzedaniu odpowiedzialności. Na przykład, sugeruje to, że kompilator powinien teraz zezwolić na programista decyduje, czy obiekt jest null. Ale dokonywanie takiej determinacji naprawdę powinno być zadaniem kompilatora.

PS: od 2003 roku rozwijam własne wyjątki, więc mogę je rzucać, jak chcę. Uważam to za najlepszą praktykę.

 1
Author: Bellash,
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
2014-04-25 12:54:40