Debugowanie.Assert vs Exception Throwing

Przeczytałem mnóstwo artykułów (i kilka innychpodobnych pytań, które zostały opublikowane na StackOverflow) o tym, jak i kiedy używać twierdzeń, i dobrze je zrozumiałem. Ale nadal nie rozumiem, jaka motywacja powinna mnie napędzać do używania Debug.Assert zamiast rzucania zwykłego wyjątku. Chodzi mi o to, że w. NET domyślną odpowiedzią na nieudane twierdzenie jest "stop the world" i wyświetlenie użytkownikowi pola wiadomości. Chociaż tego rodzaju zachowanie można modyfikować, Uważam to za bardzo irytujące i zbędne aby to zrobić, podczas gdy mogę zamiast tego, po prostu rzucić odpowiedni wyjątek. W ten sposób mogę łatwo zapisać błąd w dzienniku aplikacji tuż przed wyrzuceniem wyjątku, a ponadto moja aplikacja nie musi się zamrażać.

Więc dlaczego miałbym, jeśli w ogóle, używać Debug.Assert zamiast zwykłego wyjątku? Umieszczenie twierdzenia tam, gdzie nie powinno być, może po prostu spowodować wszelkiego rodzaju "niechciane zachowanie", więc z mojego punktu widzenia naprawdę nic nie zyskuję, używając twierdzenia zamiast rzucania wyjątku. Zgadzasz się ze mną, czy coś mi umyka?

Uwaga: w pełni rozumiem, jaka jest różnica "w teorii" (debugowanie vs Wydanie, wzorce użytkowania itp.), ale jak ja to widzę, to lepiej byłoby rzucić wyjątek, zamiast wykonywać twierdzenie. Ponieważ jeśli błąd zostanie wykryty w wydaniu produkcyjnym, nadal chciałbym, aby "assertion" nie powiodło się (w końcu "overhead" jest śmiesznie mały), więc lepiej będzie rzucać zamiast tego wyjątek.


Edit: sposób, w jaki to widzę, jeśli assert nie powiodło się, oznacza to, że aplikacja weszła w jakiś rodzaj uszkodzonego, nieoczekiwanego stanu. Dlaczego miałbym kontynuować egzekucję? Nie ma znaczenia, czy aplikacja działa w wersji debugowej czy release. To samo dotyczy obu

Author: Backwards_Dave, 2009-09-23

7 answers

Chociaż zgadzam się, że Twoje rozumowanie jest wiarygodne - to znaczy, jeśli twierdzenie zostanie nieoczekiwanie naruszone, ma sens wstrzymanie wykonania przez rzucanie -- osobiście nie użyłbym WYJĄTKÓW w miejsce twierdzeń. Oto dlaczego:

Jak mówili inni, twierdzenia powinny dokumentować sytuacje, które są niemożliwe , w taki sposób, że jeśli dojdzie do rzekomo niemożliwej sytuacji, deweloper jest informowany. Wyjątki natomiast stanowią mechanizm przepływu sterowania w sytuacjach wyjątkowych, mało prawdopodobnych lub błędnych, ale nie w sytuacjach niemożliwych. Dla mnie najważniejsza różnica jest taka:

  • Zawsze powinno być możliwe wyprodukowanie przypadku testowego, który wykonuje daną instrukcję rzutu. Jeśli nie jest możliwe stworzenie takiego przypadku testowego, to masz ścieżkę kodu w swoim programie, która nigdy nie jest wykonywana i powinna zostać usunięta jako martwy kod.

  • Nigdy nie powinno być możliwe wyprodukowanie przypadku testowego, który powoduje / align = "left" / Jeśli wywołane jest twierdzenie, albo kod jest błędny, albo twierdzenie jest błędne; tak czy inaczej, coś musi się zmienić w kodzie.

Dlatego nie zamieniłbym twierdzenia na wyjątek. Jeśli twierdzenie nie może zostać uruchomione, to zastąpienie go wyjątkiem oznacza, że masz w swoim programie nieoznaczoną ścieżkę kodu . Nie lubię nietestowalnych ścieżek kodu.

 159
Author: Eric Lippert,
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
2009-09-23 20:39:49

Twierdzenia służą do sprawdzania zrozumienia świata przez programistę. Twierdzenie powinno zawieść tylko wtedy, gdy programista zrobił coś złego. Na przykład nigdy nie używaj twierdzenia do sprawdzania danych wejściowych użytkownika.

Testuje warunki, które "nie mogą się wydarzyć". Wyjątki są dla warunków, które "nie powinny się zdarzyć, ale zrobić".

Twierdzenia są użyteczne, ponieważ w czasie budowania (lub nawet uruchomienia) można zmienić ich zachowanie. Na przykład, często w release builds, the asserts nie są nawet sprawdzane, ponieważ wprowadzają niepotrzebne koszty. Jest to również coś, na co należy uważać: twoje testy mogą nawet nie zostać wykonane.

Jeśli używasz WYJĄTKÓW zamiast twierdzeń, tracisz pewną wartość:

  1. Kod jest bardziej zwięzły, ponieważ Testowanie i rzucanie wyjątku to co najmniej dwie linie, podczas gdy assert to tylko jedna.

  2. Twój kod testowy i Rzutowy zawsze będzie uruchamiany, podczas gdy asserts może być kompilowany.

  3. Tracisz trochę komunikacja z innymi programistami, ponieważ twierdzenia mają inne znaczenie niż Kod produktu, który sprawdza i rzuca. Jeśli naprawdę testujesz twierdzenie programistyczne, użyj twierdzenia.

Więcej tutaj: http://nedbatchelder.com/text/assert.html

 15
Author: Ned Batchelder,
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
2009-09-23 17:56:37

EDIT: w odpowiedzi na edit/notkę, którą napisałeś w swoim poście: Wygląda na to, że używanie wyjątków jest właściwą rzeczą do wykorzystania nad używaniem twierdzeń dla tego typu rzeczy, które próbujesz osiągnąć. Myślę, że mentalną przeszkodą, którą uderzasz, jest to, że rozważasz wyjątki i twierdzenia, aby spełnić ten sam cel, a więc próbujesz dowiedzieć się, który z nich byłby "właściwy" do użycia. Chociaż mogą występować pewne nakładania się na sposób użycia twierdzeń i WYJĄTKÓW, nie myl, że dla nich są różne rozwiązania tego samego problemu - nie są. twierdzenia i wyjątki każdy ma swój własny cel, mocne i słabe strony.

Miałem napisać odpowiedź własnymi słowami, ale to robi pojęcie lepiej sprawiedliwości niż ja bym miał:

C # Station: Assertions

Użycie twierdzeń assert może być skuteczny sposób na złapanie logiki programu błędy w czasie wykonywania, a jednak są łatwo odfiltrowane produkcji kod. Po zakończeniu rozwoju, koszt wykonania tych zbędnych testy na błędy kodowania mogą być wyeliminowane po prostu przez zdefiniowanie symbol preprocesora NDEBUG [który wyłącza wszystkie twierdzenia] podczas kompilacja. Pamiętaj jednak, aby pamiętaj, że kod umieszczony w twierdzenie zostanie pominięte w wersja produkcyjna.

Twierdzenie jest najlepiej używane do badania warunek tylko wtedy, gdy wszystkie następny hold:

* the condition should never be false if the code is correct,
* the condition is not so trivial so as to obviously be always true, and
* the condition is in some sense internal to a body of software.

Twierdzenia prawie nigdy nie powinny być używane do wykrywania sytuacji, które powstają podczas normalnej pracy oprogramowania. Na przykład, zwykle twierdzenia powinny nie może być używany do sprawdzania błędów w wejście użytkownika. Może jednak sprawić, że sens używania twierdzeń do weryfikacji, że rozmówca sprawdził już u użytkownika wejście.

Zasadniczo, używaj wyjątków dla rzeczy, które muszą być przechwytywane/rozpatrywane w aplikacji produkcyjnej, używaj twierdzeń do wykonywania logicznych kontroli, które będzie przydatny do rozwoju, ale wyłączony w produkcji.

 11
Author: Tom Neyland,
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
2009-09-23 18:10:45

Myślę, że (wymyślony) praktyczny przykład może pomóc rozjaśnić różnicę:

(zaadaptowane z rozszerzenia wsadowego MoreLinq)

// 'public facing' method
public int DoSomething(List<string> stuff, object doohickey, int limit) {

    // validate user input and report problems externally with exceptions

    if(stuff == null) throw new ArgumentNullException("stuff");
    if(doohickey == null) throw new ArgumentNullException("doohickey");
    if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");

    return DoSomethingImpl(stuff, doohickey, limit);
}

// 'developer only' method
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {

    // validate input that should only come from other programming methods
    // which we have control over (e.g. we already validated user input in
    // the calling method above), so anything using this method shouldn't
    // need to report problems externally, and compilation mode can remove
    // this "unnecessary" check from production

    Debug.Assert(stuff != null);
    Debug.Assert(doohickey != null);
    Debug.Assert(limit > 0);

    /* now do the actual work... */
}
Eric Lippert i inni powiedzieli, że twierdzisz tylko rzeczy, które oczekujesz, że będą poprawne, na wszelki wypadek ty (programista) przypadkowo użyłeś go źle gdzie indziej, więc możesz naprawić swój kod. W zasadzie rzucasz wyjątki, gdy nie masz kontroli lub nie możesz przewidzieć, co się stanie, np. dla wejścia użytkownika , tak aby cokolwiek dało złe dane, mogło odpowiednio zareagować (np. użytkownik).
 6
Author: drzaus,
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
2017-05-23 10:31:33

Kolejny nugget z Code Complete :

" twierdzenie jest funkcją lub makrem to narzeka głośno, jeśli założenie to nieprawda. Używaj twierdzeń do dokumentowania założenia przyjęte w kodzie i do spłukiwania w nieoczekiwanych warunkach. ...

" w trakcie rozwoju, twierdzenia z przeciwstawnych założeń, nieoczekiwane warunki, złe wartości przekazywane do procedur, i tak dalej."

Dodaje kilka wskazówek co powinno i powinno nie twierdzę.

Z drugiej strony wyjątki:

" użyj obsługi wyjątków do rysowania uwaga na nieoczekiwane przypadki. W wyjątkowych przypadkach należy sposób, który czyni je oczywistymi podczas rozwój i odzyskiwanie, gdy kod produkcji jest uruchomiony."

Jeśli nie masz tej książki powinieneś ją kupić.

 4
Author: Andrew Cowenhoven,
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
2009-09-23 17:58:29

Debugowanie.Assert domyślnie działa tylko w kompilacjach debugowania, więc jeśli chcesz złapać jakieś złe, nieoczekiwane zachowanie w kompilacjach release, musisz użyć wyjątków lub włączyć stałą debugowania we właściwościach projektu (co jest ogólnie uważane za niezbyt dobry pomysł).

 0
Author: Mez,
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
2009-09-23 19:56:24

Używaj twierdzeń dla rzeczy, które możliwe, ale nie powinny się wydarzyć (jeśli byłoby to niemożliwe, dlaczego stawiasz twierdzenie?).

Czy to nie brzmi jak przypadek użycia Exception? Dlaczego używasz twierdzenia zamiast Exception?

Ponieważ powinien istnieć kod, który zostanie wywołany przed Twoim twierdzeniem, który zatrzyma parametr twierdzenia jako false.

Zazwyczaj nie ma kodu przed Exception, który gwarantuje, że nie będzie rzucony.

Dlaczego dobrze, że Debug.Assert() jest skompilowany w prod? Jeśli chcesz wiedzieć o tym w debugowaniu, nie chcesz wiedzieć o tym w prod?

Chcesz go tylko podczas tworzenia, ponieważ gdy znajdziesz Debug.Assert(false) sytuacje, piszesz kod, aby zagwarantować, że Debug.Assert(false) Więcej się nie powtórzy. Po zakończeniu rozwoju, zakładając, że znalazłeś Debug.Assert(false) sytuacje i je naprawiłeś, Debug.Assert() można bezpiecznie skompilować, ponieważ są one teraz zbędne.

 0
Author: Backwards_Dave,
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
2018-05-01 05:56:12