Jakie wyjątki należy zgłaszać w przypadku nieprawidłowych lub nieoczekiwanych parametrów w .NET?


163

Jakie typy wyjątków należy zgłaszać w przypadku nieprawidłowych lub nieoczekiwanych parametrów w .NET? Kiedy wybrałbym jeden zamiast drugiego?

Kontynuacja:

Którego wyjątku użyłbyś, gdybyś miał funkcję oczekującą liczby całkowitej odpowiadającej miesiącowi i przeszedłeś w „42”? Czy należałoby to do kategorii „poza zakresem”, mimo że nie jest to kolekcja?

Odpowiedzi:


249

Lubię używać: ArgumentException, ArgumentNullException, i ArgumentOutOfRangeException.

Istnieją również inne opcje, które nie koncentrują się tak bardzo na samym argumencie, ale raczej oceniają połączenie jako całość:

  • InvalidOperationException- Argument może być prawidłowy, ale nie w bieżącym stanie obiektu. Kredyt trafia do STW (wcześniej Yoooder). Zagłosuj również na jego odpowiedź .
  • NotSupportedException- Przekazane argumenty są prawidłowe, ale po prostu nie są obsługiwane w tej implementacji. Wyobraź sobie klienta FTP i wydajesz polecenie, którego klient nie obsługuje.

Sztuczka polega na rzuceniu wyjątku, który najlepiej wyraża, dlaczego metody nie można nazwać taką, jaka jest. W idealnym przypadku wyjątek powinien zawierać szczegółowe informacje o tym, co poszło nie tak, dlaczego jest nie tak i jak to naprawić.

Uwielbiam, gdy komunikaty o błędach wskazują pomoc, dokumentację lub inne zasoby. Na przykład firma Microsoft wykonała dobry pierwszy krok z artykułami KB, np. „Dlaczego otrzymuję komunikat o błędzie„ Operacja przerwana ”, gdy odwiedzam stronę sieci Web w programie Internet Explorer?” . Gdy napotkasz błąd, wskażą Ci artykuł KB w komunikacie o błędzie. To, czego nie robią dobrze, to to, że nie mówią ci, dlaczego to się nie udało.

Jeszcze raz dziękuję STW (ex Yoooder) za komentarze.


W odpowiedzi na Twoją kontynuację rzuciłbym plik ArgumentOutOfRangeException. Zobacz, co MSDN mówi o tym wyjątku:

ArgumentOutOfRangeExceptionjest generowany, gdy wywoływana jest metoda i co najmniej jeden z argumentów przekazanych do metody nie jest odwołaniem o wartości null ( Nothingw języku Visual Basic) i nie zawiera prawidłowej wartości.

Zatem w tym przypadku przekazujesz wartość, ale nie jest to prawidłowa wartość, ponieważ Twój zakres to 1–12. Jednak sposób, w jaki go dokumentujesz, wyjaśnia, co rzuca twój interfejs API. Bo chociaż mógłbym powiedzieć ArgumentOutOfRangeException, inny programista mógłby powiedzieć ArgumentException. Ułatw to i udokumentuj zachowanie.


Psst! Zgadzam się, że odpowiedziałeś dokładnie poprawnie na jego konkretne pytanie, ale zobacz moją odpowiedź poniżej, aby uzupełnić kodowanie obronne i walidację parametrów ;-D
STW

+1, ale udokumentowanie, który wyjątek został zgłoszony i dlaczego, jest ważniejsze niż wybranie tego „właściwego”.
pipTheGeek

@pipTheGeek - myślę, że to naprawdę kwestia dyskusyjna. Chociaż dokumentowanie jest zdecydowanie ważne, oczekuje również, że konsumujący programista będzie proaktywny lub defensywny i faktycznie dokładnie zapozna się z dokumentacją. Wybrałbym przyjazny / opisowy błąd zamiast dobrej dokumentacji, ponieważ użytkownik końcowy ma szansę zobaczyć jeden z nich, a nie drugi; istnieje większa szansa, że ​​użytkownik końcowy przekaże słaby programista opisowy błąd niż słaby programista czytający pełną dokumentację
STW

4
Uwaga: jeśli złapiesz ArgumentException, również przechwyci ArgumentOutOfRange.
Steven Evers

Co powiesz na FormatException: wyjątek, który jest generowany, gdy format argumentu jest nieprawidłowy lub gdy ciąg formatu złożonego nie jest poprawnie sformułowany.
Anthony,

44

Głosowałem za odpowiedzią Josha , ale chciałbym dodać jeszcze jedną do listy:

System.InvalidOperationException należy zgłosić, jeśli argument jest prawidłowy, ale obiekt jest w stanie, w którym argument nie powinien być używany.

Aktualizacja pobrana z MSDN:

InvalidOperationException jest używany w przypadkach, gdy niepowodzenie wywołania metody jest spowodowane przyczynami innymi niż nieprawidłowe argumenty.

Powiedzmy, że twój obiekt ma metodę PerformAction (akcja enmSomeAction), prawidłowe enmSomeActions to Otwórz i Zamknij. Jeśli wywołasz PerformAction (enmSomeAction.Open) dwa razy z rzędu, drugie wywołanie powinno zgłosić wyjątek InvalidOperationException (ponieważ arugment był prawidłowy, ale nie dla bieżącego stanu formantu)

Ponieważ już robisz właściwą rzecz, programując defensywnie, mam jeszcze jeden wyjątek, o którym warto wspomnieć, to ObjectDisposedException. Jeśli twój obiekt implementuje IDisposable, to zawsze powinieneś mieć zmienną klasy śledzącą stan usunięty; jeśli twój obiekt został usunięty i zostanie wywołana metoda, powinieneś zgłosić ObjectDisposedException:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Aktualizacja: Odpowiadając na dalsze uwagi: sytuacja jest trochę niejednoznaczna i jest trochę bardziej skomplikowana przez ogólny (nie w sensie .NET Generics) typ danych używany do reprezentowania określonego zestawu danych; wyliczenie lub inny silnie wpisany obiekt byłby bardziej idealny - ale nie zawsze mamy taką kontrolę.

Osobiście skłaniałbym się do ArgumentOutOfRangeException i udostępnił komunikat wskazujący, że prawidłowe wartości to 1-12. Moje rozumowanie jest takie, że kiedy mówisz o miesiącach, zakładając, że wszystkie liczby całkowite miesięcy są prawidłowe, to oczekujesz wartości z zakresu 1-12. Gdyby tylko niektóre miesiące (takie jak miesiące, które miały 31 dni) były ważne, nie miałbyś do czynienia z Range per se i rzuciłbym ogólny ArgumentException, który wskazywałby prawidłowe wartości, a także udokumentowałbym je w komentarzach do metody.


1
Słuszna uwaga. To może wyjaśniać różnicę między nieprawidłowymi i nieoczekiwanymi danymi wejściowymi. +1
Daniel Brückner

Psst, zgadzam się z tobą, po prostu nie zamierzałem ukraść twojego grzmotu. Ale skoro to wskazałeś, zaktualizowałem moją odpowiedź
JoshBerke

38

W zależności od rzeczywistej wartości i tego, który wyjątek pasuje najlepiej:

Jeśli to nie jest wystarczająco dokładne, po prostu wyprowadź własną klasę wyjątków z ArgumentException.

Odpowiedź Yooodera oświeciła mnie. Dane wejściowe są niepoprawne, jeśli są nieprawidłowe w dowolnym momencie, natomiast dane wejściowe są nieoczekiwane, jeśli nie są poprawne w bieżącym stanie systemu. Więc w późniejszym przypadku InvalidOperationExceptionwybór jest rozsądny.


6
Zaczerpnięte ze strony MSDN w InvalidOperationException: „InvalidOperationException jest używany w przypadkach, gdy niepowodzenie wywołania metody jest spowodowane przyczynami innymi niż nieprawidłowe argumenty.”
STW


3

ArgumentException :

ArgumentException jest generowany, gdy wywoływana jest metoda i co najmniej jeden z przekazanych argumentów nie spełnia specyfikacji parametru wywoływanej metody. Wszystkie wystąpienia ArgumentException powinny zawierać zrozumiały komunikat o błędzie opisujący nieprawidłowy argument, a także oczekiwany zakres wartości argumentu.

Istnieje również kilka podklas dla określonych rodzajów inwalidztwa. Link zawiera podsumowania podtypów i kiedy powinny mieć zastosowanie.


1

Krótka odpowiedź:
żadne

Dłuższa odpowiedź:
używanie Argument * Wyjątek (z wyjątkiem biblioteki, w której jest włączony produkt, takiej jak biblioteka komponentów) to zapach. Wyjątki dotyczą sytuacji wyjątkowych, a nie błędów, a nie błędów użytkownika (tj. Konsumenta API).

Najdłuższa odpowiedź:
rzucanie wyjątków dla nieprawidłowych argumentów jest niegrzeczne, chyba że napiszesz bibliotekę.
Wolę używać asercji z dwóch (lub więcej) powodów:

  • Asercje nie muszą być testowane, podczas gdy asercje rzutów tak, a testowanie z ArgumentNullException wygląda śmiesznie (spróbuj).
  • Asercje lepiej informują o przeznaczeniu jednostki i są bliżej bycia wykonywalną dokumentacją niż specyfikacją zachowania klasy.
  • Możesz zmienić zachowanie naruszenia asercji. Na przykład w kompilacji debugowania okno komunikatu jest w porządku, więc QA uderzy cię od razu (dostajesz również zerwanie IDE w wierszu, w którym to się dzieje), podczas gdy w teście jednostkowym możesz wskazać błąd asercji jako błąd testu .

Oto jak wygląda obsługa wyjątku zerowego (oczywiście sarkastyczny):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

Wyjątki są stosowane, gdy sytuacja jest oczekiwana, ale wyjątkowa (zdarzają się rzeczy, na które konsument nie ma wpływu, np. Awaria IO). Argument * Wyjątek wskazuje na błąd i powinien być (moim zdaniem) obsługiwany za pomocą testów i wspomagany debugowaniem.

BTW: W tym konkretnym przypadku możesz użyć typu Month zamiast int. C # nie spełnia wymagań, jeśli chodzi o bezpieczeństwo typów (Aspect # rulez!), Ale czasami można zapobiec (lub złapać w czasie kompilacji) te błędy razem.

I tak, MicroSoft się co do tego myli.


6
IMHO, wyjątki powinny być również zgłaszane, gdy wywoływana metoda nie może w rozsądny sposób kontynuować. Obejmuje to przypadek, gdy dzwoniący przekazał fałszywe argumenty. Co byś zrobił zamiast tego? Zwrócić -1?
John Saunders

1
Jeśli nieprawidłowe argumenty spowodowałyby awarię funkcji wewnętrznej, jakie są wady i zalety testowania argumentów pod kątem ważności, w porównaniu do wychwytywania wyjątku InvalidArgumentException z funkcji wewnętrznej i pakowania go bardziej informacyjnym? To drugie podejście wydaje się poprawiać wydajność w typowym przypadku, ale nie widziałem, aby zrobiło to zbyt wiele.
supercat

Szybkie wyszukiwanie w Google wokół tego pytania wskazuje, że rzucenie ogólnego Wyjątku jest najgorszą praktyką ze wszystkich. Jeśli chodzi o potwierdzenie argumentu, widzę w tym zalety w małych projektach osobistych, ale nie w aplikacjach korporacyjnych, w których nieprawidłowe argumenty są najprawdopodobniej spowodowane złą konfiguracją lub słabym zrozumieniem aplikacji.
Maksymalnie

0

Istnieje standardowy wyjątek ArgumentException, którego możesz użyć lub możesz utworzyć podklasę i utworzyć własną. Istnieje kilka określonych klas ArgumentException:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Którykolwiek działa najlepiej.


1
Nie zgadzam się w prawie wszystkich przypadkach; Dostarczone klasy wyjątków .NET Argument * Exception są bardzo często używane i zapewniają możliwość dostarczenia wystarczająco szczegółowych informacji, aby powiadomić konsumenta o problemie.
STW

Dla wyjaśnienia - nie zgadzam się z prawie wszystkimi przypadkami wyprowadzania z klas Argument * Exception. Użycie jednego z wyjątków argumentów platformy .NET oraz opisowego i przejrzystego komunikatu zapewnia wystarczającą ilość szczegółów dla mniej więcej każdej sytuacji, w której argumenty są nieprawidłowe.
STW

zgodził się, ale właśnie opisywałem dostępne opcje. Zdecydowanie powinienem był bardziej precyzyjnie określić „preferowaną” metodę.
Scott M.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.