2008-09-14 11 views
33

Właśnie zacząłem przeglądać "Debugowanie aplikacji MS .Net 2.0" Johna Robbinsa, i dezorientowałem się jego ewangelizacją dla Debug.Assert (...).Debug.Assert vs. Specific Thrown Exceptions

Wskazuje on, że dobrze wdrożony twierdzi przechowywania stanu, nieco, o wystąpieniu błędu, np:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter"); 

Teraz osobiście wydaje się szalony do mnie, że on tak kocha przekształcenie jego testu bez przeprowadzenia prawdziwy rozsądny "logiki biznesowej" komentarz, być może "i < = 3 nigdy nie może się zdarzyć z powodu procesu klonowania się przed flobittyjam".

Sądzę, że dostaję Asserts jako rodzaj niskiego poziomu "Chrońmy swoje założenia", coś w rodzaju ... zakładając, że czujesz, że jest to test, który musisz wykonać tylko w debugowaniu - np. chroniąc siebie przed kolegami i przyszłymi programistami, mając nadzieję, że oni faktycznie przetestują rzeczy.

Ale to, czego nie rozumiem, mówi, że oprócz zwykłej obsługi błędów powinieneś używać twierdzeń; teraz wyobrażam sobie coś takiego:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status"); 
if (i <= 3) 
{ 
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString()); 
} 

Co zyskałem dzięki debugowaniu. Czy powtórzenie testu warunku błędu? Myślę, że mam to, gdybyśmy mówili o debugowania tylko dwukrotne sprawdzenie bardzo ważne obliczenia ...

double interestAmount = loan.GetInterest(); 
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc"); 

... ale ja nie rozumiem do badań parametrów, które są na pewno warto sprawdzanie (zarówno w buildach DEBUG, jak i Release) ... lub nie. czego mi brakuje?

Odpowiedz

48

Asercje nie służą do sprawdzania parametrów. Sprawdzanie parametrów powinno zawsze odbywać się (i dokładnie zgodnie z określonymi warunkami wstępnymi podanymi w dokumentacji i/lub specyfikacji), a następnie, jeśli jest to konieczne, wyrzucać ArgumentOutOfRangeException.

Asercje są przeznaczone do testowania sytuacji "niemożliwych", tj. Rzeczy, które (zgodnie z logiką programu) przyjmują są prawdziwe. Twierdzenia są po to, aby powiedzieć, czy te założenia są zerwane z jakiegokolwiek powodu.

Mam nadzieję, że to pomoże!

+10

Asercje mogą być wykorzystywane do kontroli parametrów * wewnętrznej * połączeń metody (zwanego kodem należących do tego samego elementu) metody, w przeciwieństwie do zewnętrznych połączeń metody (nazywanych przez inny składnik) . Na przykład mogę stwierdzić, że parametr metody prywatnej typu Double nie jest NaN. – RoadWarrior

+0

@ Chris: Podajesz, że asercje nie mają być używane do sprawdzania parametrów. Czy istnieje ku temu powód? Mam tendencję do zgłaszania wyjątków do sprawdzania parametrów, szczególnie w konstruktorach, gdy wstrzykiwam zależne obiekty. Jednak mówiono mi o używaniu Asercji. Nie mam logicznego wytłumaczenia dla używania wyjątków, a nie asercji. Czy jesteś w stanie wyjaśnić? Pozdrawiam –

+7

Nie martw się, dowiedziałem się, dlaczego. Według Jona Skeeta http://stackoverflow.com/questions/1276308/exception-vs-assertion: "Używaj asercji dla wewnętrznych sprawdzeń logicznych w twoim kodzie oraz normalnych wyjątków dla warunków błędu poza kontrolą twojego bezpośredniego kodu." –

2

IMO to strata czasu opracowywania. Odpowiednio zaimplementowany wyjątek daje jasny obraz tego, co się stało. Widziałem zbyt wiele aplikacji pokazujących niejasne błędy "Asercja nie powiodło się: i < 10". Postrzegam twierdzenie jako tymczasowe rozwiązanie. Moim zdaniem żadne twierdzenia nie powinny być w ostatecznej wersji programu. W mojej praktyce użyłem twierdzeń dla szybkich i brudnych czeków. Ostateczna wersja kodu powinna brać pod uwagę błędną sytuację i odpowiednio się zachowywać. Jeśli dzieje się coś złego, masz dwie możliwości: załatw je lub odejdź. Funkcja powinna rzucić wyjątek z sensownym opisem, jeśli zostaną przekazane błędne parametry. Nie widzę żadnych punktów w powielaniu logiki sprawdzania poprawności.

4

Używam jawnych kontroli, które wyrzucają wyjątki na publicznych i chronionych metod i asercji na prywatnych metodach.

Zazwyczaj jawne kontrole chronią prywatne metody przed wyświetlaniem nieprawidłowych wartości. Tak naprawdę, twierdzenie sprawdza stan, który powinien być niemożliwy. Jeśli aserowanie się uruchamia, mówi mi, że w logice walidacji znajduje się wada jednej z publicznych procedur na klasie.

17

Istnieje aspekt komunikacji, który zapewnia rzucanie wyjątków i wyjątków.

Załóżmy, że mamy klasę użytkownika z właściwością Nazwa i metodą ToString.

Jeśli ToString jest realizowany tak:

public string ToString() 
{ 
    Debug.Assert(Name != null); 
    return Name; 
} 

Mówi, że nigdy nie powinno Nazwa nieważna i nie jest to błąd w klasie użytkownika, jeśli jest.

Jeśli ToString jest wdrożenie takiego:

public string ToString() 
{ 
    if (Name == null) 
    { 
      throw new InvalidOperationException("Name is null"); 
    } 

    return Name; 
} 

Mówi, że rozmówca jest za pomocą ToString nieprawidłowo, jeśli nazwa jest nieważna i należy sprawdzić, czy przed wywołaniem.

Realizacja zarówno

public string ToString() 
{ 
    Debug.Assert(Name != null); 
    if (Name == null) 
    { 
      throw new InvalidOperationException("Name is null"); 
    } 

    return Name; 
} 

mówi, że jeśli nazwa jest null tam bug w klasie użytkownika, ale chcemy, aby obsłużyć tak. (Użytkownik nie musi sprawdzać nazwy przed wywołaniem.) Myślę, że jest to rodzaj bezpieczeństwa, który zalecał Robbins.

3

Wyjątek można złapać i połknąć, co powoduje, że błąd jest niewidoczny podczas testowania. To nie może się zdarzyć z Debug.Assert.

Nikt nigdy nie powinien mieć chwytacza, który przechwytuje wszystkie wyjątki, ale ludzie i tak to robią, a czasami jest to nieuniknione. Jeśli twój kod zostanie wywołany z COM, warstwa interop przechwyci wszystkie wyjątki i zamieni je na kody błędów COM, co oznacza, że ​​nie zobaczysz nieobsługiwanych wyjątków. Asserts nie cierpi z tego powodu.

Również, gdy wyjątek byłby nieobsługiwany, jeszcze lepszą praktyką jest wykonanie mini-zrzutki. Jednym z obszarów, w którym VB jest silniejszy niż C#, jest możliwość użycia filtra wyjątków do przyciągnięcia mini-dumpa, gdy wyjątek jest w locie, i pozostawienie pozostałej części obsługi wyjątku w niezmienionej postaci. Gregg Miskelly's blog post on exception filter inject zapewnia przydatny sposób zrobienia tego z poziomu C#.

Jeszcze jedna uwaga na temat aktywów ... źle się sprawdzały, testując warunki błędów w kodzie. Warto mieć owijkę do wyłączenia asertywności dla testów jednostkowych.

0

Oto 2 centy.

Myślę, że najlepszym sposobem jest użycie obu twierdzeń i wyjątków. Główne różnice między tymi dwiema metodami, imho, jeśli te instrukcje Assert można łatwo usunąć z tekstu aplikacji (definiuje, atrybuty warunkowe ...), a Wyjątek jest zależny (typowo) od kodu warunkowego, który jest trudniejszy do usunięcia (sekcja wielordzeniowa z warunkami wstępnymi preprocesora).

Każdy wyjątek aplikacji powinien być traktowany poprawnie, podczas gdy potwierdzenia będą spełnione tylko podczas opracowywania i testowania algorytmu.

Jeśli przekazujesz odwołanie do obiektu o wartości zerowej jako parametr rutynowy i używasz tej wartości, otrzymasz wyjątek wskaźnika pustego. Rzeczywiście: dlaczego powinieneś napisać twierdzenie? W tym przypadku jest to strata czasu. Ale co z członkami prywatnej klasy używanymi w procedurach klasowych? Gdy te wartości są gdzieś ustawione, lepiej sprawdzić przy pomocy asercji, jeśli ustawiona jest wartość pusta. Dzieje się tak tylko dlatego, że gdy użyjesz elementu, otrzymasz wyjątek wskaźnika pustego, ale nie wiesz, w jaki sposób ustawiono wartość. Powoduje to ponowne uruchomienie programu we wszystkich punktach wejścia, aby ustawić członka prywatnego.

Wyjątki są bardziej przydatne, ale mogą być (imho) bardzo ciężkie do zarządzania i istnieje możliwość korzystania z zbyt wielu wyjątków. I wymagają dodatkowej kontroli, być może niepożądanej, aby zoptymalizować kod. Osobiście używam wyjątków tylko wtedy, gdy kod wymaga głębokiej kontroli przechwytywania (instrukcje catch są bardzo niskie w stosie wywołań) lub gdy parametry funkcji nie są zakodowane na stałe w kodzie.

4

Zastanawiam się nad tym długo i ciężko, jeśli chodzi o dostarczanie wskazówek dotyczących debugowania kontra zapewnienia w odniesieniu do problemów związanych z testowaniem.

Powinieneś być w stanie przetestować swoją klasę z błędnego wprowadzenia, złym stanie, nieprawidłowej kolejności operacji i wszelkich innych możliwych stan błędu i dochodzić powinien nigdy podróży. Każde potwierdzenie sprawdza, czy wartość powinna być prawdziwa, niezależnie od wprowadzonych danych lub obliczeń.

Dobre zasady kciuka Mam przybył:

  1. twierdzi nie są zamiennikiem dla solidnej kod, który działa poprawnie niezależnie od konfiguracji. Są komplementarne.

  2. Potwierdzenia nigdy nie powinny być potknięte podczas testu jednostkowego, nawet w przypadku podawania nieprawidłowych wartości lub sprawdzania warunków błędu. Kod powinien obsługiwać te warunki bez występowania asercji.

  3. Jeśli asercja się potknie (w teście jednostkowym lub podczas testu), klasa jest błędna.

Dla wszystkich innych błędów - zwykle do środowiska (połączenie sieciowe utracone) lub nadużycia (wywołujący przeszedł wartość null) - to o wiele ładniejszy i bardziej zrozumiałe używać twardych weryfikacje & wyjątki. Jeśli wystąpi wyjątek, osoba dzwoniąca wie, że prawdopodobnie jest to ich wina. Jeśli pojawi się potwierdzenie, osoba dzwoniąca wie, że jest to prawdopodobnie błąd w kodzie, w którym znajduje się asercja.

Jeśli chodzi o powielanie: Zgadzam się. Nie widzę powodu, dla którego miałbyś replikować sprawdzanie poprawności za pomocą debugowania i sprawdzania wyjątków. Nie tylko dodaje szumu do kodu i mętnieje w wodzie, jeśli chodzi o winnego, ale jest formą powtórzenia.

1

Przykład dobrego wykorzystania dochodzić:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry 
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning 

Osobiście uważam, że Assert powinny tylko być stosowane, gdy wiesz, że coś jest poza pożądane limity, ale możesz być pewien, że to dość bezpieczne dalej. We wszystkich innych okolicznościach (nie zwracaj uwagi na okoliczności, o których nie myślałem) korzystaj z wyjątków, aby szybko i sprawnie zawieść.

Najważniejsza dla mnie kompromitacja to to, czy chcesz zniszczyć system Live/Production z wyjątkiem, aby uniknąć korupcji i ułatwić rozwiązywanie problemów, czy też napotkasz sytuację, która nigdy nie powinna być kontynuowana niezauważona w teście/wersje debugowania, ale można zezwolić na kontynuowanie produkcji (rejestrowanie ostrzeżenia oczywiście).

cf.http://c2.com/cgi/wiki?FailFast kopiowane i modyfikowany Java pytania: Exception Vs Assertion

Powiązane problemy