Debugowanie.Assert vs Exceptions

O dziwo udało mi się znaleźć tylko jedno poprzednie pytanie na temat SO na ten temat, i chciałbym po prostu uzyskać społeczność "wotum zaufania" (lub nie!) na moim podejściu.

Tak to widzę:

  • użyj Debug.Assert, aby określić rzeczy, których oczekujesz, że będą prawdziwe. Można to wykorzystać, gdy mamy pełną kontrolę nad naszym środowiskiem, np. w metodzie sprawdź niektóre warunki przed i po.
  • użyj WYJĄTKÓW, gdy zaistnieją wyjątkowe okoliczności. Radzenie sobie z zewnętrznymi zasobami, tj. plikami, bazami danych, sieciami itp. Ale...

Robi się trochę mętny w poniższym scenariuszu. Należy pamiętać, że jest to wymyślony przykład tylko dla ilustracji!

Powiedzmy, że mamy klasę MyClass, która ma publiczną właściwość MyMode i metodę GetSomeValueForCurrentMode(). Rozważmy MyClass jako wersję przeznaczoną do wysłania (release built) w bibliotece do użytku przez innych programistów.

Oczekujemy, że MyMode będzie aktualizowany przez zewnętrznych użytkowników tego klasy. Teraz GetSomeValueForCurrentMode() ma następującą logikę:

switch(MyMode)
{
case Mode.ModeA:
return val1;
case Mode.ModeB:
return val2;
default:
//Uh-uh this should never happen

}

Chodzi mi o to, że użytkownik MyClass zostawił go w nieprawidłowym stanie. Więc co powinniśmy zrobić?

W domyślnym, powinniśmy Debug.Assert lub throw new InvalidOperationException (lub inne) ?

Jest jedna mantra, która mówi, że nie powinniśmy ufać użytkownikom naszych klas. Jeśli wybierzemy Debug.Assert I built MyClass jako release build (usuwając tym samym Asserts Debug) użytkownik klasy NIE otrzymałby przydatnych informacji, że ją opuścił w stanie nieważnym. Ale jest to w pewnym sensie sprzeczne z inną mantrą, która mówi, że wyrzuca wyjątki tylko wtedy, gdy dzieje się coś całkowicie poza Twoją kontrolą.

Uważam, że kręcę się w kółko z tą-jedną z tych debat programistycznych, które nie wydają się mieć definitywnej 'poprawnej' odpowiedzi. Więc poddajmy to pod głosowanie!

Edit: zauważyłem tę odpowiedź w pokrewnym pytaniu SO (projekt według umowy z wykorzystaniem twierdzeń lub WYJĄTKÓW?):

Zasada jest że powinieneś używać twierdzeń, gdy próbujesz wyłapać własne błędy, i WYJĄTKÓW, gdy próbujesz wyłapać błędy innych ludzi. Innymi słowy, należy użyć WYJĄTKÓW, aby sprawdzić warunki wstępne dla funkcji publicznego API i za każdym razem, gdy otrzymasz jakiekolwiek dane, które są zewnętrzne do Twojego systemu. Należy używać asserts dla funkcji lub danych wewnętrznych systemu.

Dla mnie ma to sens i może być połączone z opisaną techniką "Assert then throw" poniżej.

Myśli mile widziane!

Author: Community, 2009-02-03

8 answers

Zgadzam się z większością osób tutaj i podążam za projektem po kontrakcie. Powinieneś spróbować bardzo wyraźnie odróżnić wymagania w wdrożonym kodzie (Kontrakty) od określenia oczekiwanego stanu podczas projektowania (asercje debugowania).

Należy zawsze rzucać twierdzenia umowne jako wyjątki(ponieważ zawsze powinny być wyjątkowe). W większości frameworków wbudowane są mechanizmy wychwytywania asercji debugowania. Ale w czasie wykonywania zawsze należy rzucać wyjątek.

Używam custom library to help with this (in C# / VB. NET). I recently put up on Codeplex (http://www.contractdriven.com / ) Jeśli interesuje Cię jak to działa w praktyce.

Poboczną zaletą tego jest to, że gdy zaczniesz używać DbC bardziej regularnie, rzadko musisz używać asercji debugowania, ponieważ istnieją już wyraźne gwarancje zapisane w Twoim kodzie, więc naprawdę trudno jest dostać się do nieprawidłowego stanu.

Więc pytanie w oryginalnym poście... "What I' m do tego dochodzi fakt, że użytkownik MyClass zostawił go w nieprawidłowym stanie. Więc co powinniśmy zrobić?"...nigdy nie powinien powstać.

Być może już nigdy nie będziesz musiał niczego debugować! ;-)

 5
Author: ChrisV,
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-02-03 16:13:16

Po pierwsze, Ważność MyClass powinna być oczywiście wyrażona przez niezmiennik MyClass .

Po Drugie, mówisz "oczekujemy, że MyMode będzie aktualizowany przez zewnętrznych użytkowników tej klasy" - oczywiście setter tego trybu powinien mieć typową formę design-by-contract (jak każda publiczna Funkcja):

  void Setter(mode m)
  {
    // INVARIANT ASSERT (1)
    // PRECONDITION ASSERTS (uses "m") (2)

    // BODY (3)

    // POSTCONDITION ASSERTS (if any) (4)
    // INVARIANT ASSERT (5)
  }

W (5) nie powiodłoby się z krzyczącym twierdzeniem, którego niezmiennik nie trzyma. ale w (2) wcześniej by Ci się nie udało, ponieważ tryb M przekazany jest nieważne. to wyśle jasny komunikat do użytkownika i w ten sposób rozwiąże twój problem.

I proszę mi nie mówić, że pole mode jest publiczne i użytkownicy zmieniają je bez żadnej kontroli.

Edit: o twierdzeniach i trybie wydania Zobacz też:

 5
Author: Daniel Daranas,
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 12:19:32

Zasadniczo zgadzam się z konkluzją twojego pytania: Jeśli AKod wszy wykryje błąd A wszy popełnione, jest to przypadek Assert (a twierdzenia powinny być pozostawione w kodzie produkcyjnym, chyba że wydajność nakazuje inaczej). Jeśli kod Alicji wykryje błąd w kodzie Eve, jest to przypadek dla Exceptions, zakładając, że Alicja i Ewa znajdują się po przeciwnych stronach Twojego oprogramowania do śledzenia błędów.

To jest ogólna zasada. Twierdzenia, w nieco zmodyfikowanej formie, mogą być również używane jako " heads-up, developer!" mechanizm (i wtedy nie powinny być nazywane "ASSERT", ale "HEADS_UP" lub coś podobnego). Co zrobić, jeśli Twoja firma opracowuje produkt klient / serwer, a serwer wyśle nieprawidłowe dane do klienta? Jeśli jesteś programistą klienta, masz ochotę traktować go jako dane zewnętrzne (To dane Eve są kaputtem) i chcesz rzucić wyjątek. Ale" łagodniejsze " twierdzenie, które sprawia, że Visual Studio zatrzymanie debuggera może być bardzo dobrym sposobem na wczesne wykrycie tych problemów i zgłoszenie ich zespołowi serwera. W prawdziwej Instalacji Może to być Mallory temperowanie danych między Eve i Alice, ale w większości przypadków jest to błąd jednego z twoich kolegów i chcesz go zobaczyć, kiedy to się stanie - dlatego nazywam je twierdzeniami "heads-up": nie zastępują wyjątków, ale dają Ostrzeżenie i szansę na sprawdzenie problemu.

 5
Author: Carl Seleborg,
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-02-03 15:30:30

Często oba: Assert, throw.

Twierdzisz, ponieważ chcesz powiadomić deweloperów o błędnym założeniu podczas rozwoju.

Rzucasz, ponieważ jeśli dzieje się tak w release build, musisz upewnić się, że system nie kontynuuje przetwarzania, gdy jest w złym stanie.

Pożądana charakterystyka niezawodności Twojego systemu może mieć wpływ na twój wybór tutaj, ale "assert then throw" jest często użyteczną strategią, myślę.

 4
Author: Brian,
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-02-03 08:24:01

Moje zastosowanie wynika z pomysłów z design by contract. Zasadniczo zapewniasz, że przychodzące parametry, zmienne globals i inne informacje o stanie są ważne podczas wprowadzania funkcji, a zwracane wartości i stan są również ważne podczas opuszczania. Jeśli na początku pojawi się błąd w kodzie wywołującym, jeśli na końcu pojawi się błąd w tym kodzie. Są to warunki wstępne i warunki post.

Twierdzenie w instrukcji switch jest tylko bardzo przydatne, jeśli sprawdziłeś swoje warunki wstępne przy wejściu. Jeśli w tym scenariuszu osiągniesz stan nieakceptowalny w środku swojej funkcji, jest to awaria funkcji lub funkcja wywołana. Osobiście trzymałbym się tu twierdzenia, ponieważ nie jest to wyjątek. Jak sugerujesz, wyjątki dotyczą zasobów, a nie błędów. Możesz jednak utworzyć niestandardowy moduł obsługi twierdzeń, który istnieje w kompilacji release i rzuca wyjątek w przypadku niepowodzenia, aby nadać programowi możliwość powrotu do stabilnego stanu.

 2
Author: Shane MacLaughlin,
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-02-03 08:16:36

Powiedziałbym: użyj wyjątku, jeśli błąd znajduje się w cudzym kodzie lub w kodzie w innym podsystemie (niezależnie od tego, czy go napisałeś, czy nie). Użyj również wyjątku, jeśli w danych pochodzących z zewnętrznego źródła, takiego jak plik, wystąpił błąd.

Czasami nie wiadomo, czy dane pochodzą z zewnętrznego źródła, czy nie. Jeśli Bezpieczeństwo jest ważne i istnieje możliwość, że masz do czynienia z danymi zewnętrznymi, które nie zostały zweryfikowane prawidłowo, użyj wyjątku. Jeśli bezpieczeństwo nie jest ważne wtedy użyłbym wydajności jako tiebreakera: czy ten kod może być wykonywany w ciasnej pętli? Jeśli tak, rozważ użycie assert, ponieważ uzyskasz lepszą wydajność w kompilacji Release. W przeciwnym razie użyj wyjątku.

 1
Author: Qwertie,
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-11-06 19:06:45

To zależy od języka, jest to jednak w Javie twierdzenia muszą być włączone, aby to działało, więc wyjątek jest lepszy. Jednak zawsze lepiej mieć konkretny wyjątek, więc tutaj powinno być Nielegalstateexception.

 0
Author: IAdapter,
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-02-03 08:11:35

W. Net, metody klas Debug nie są zawarte w programie, gdy tworzysz release build, więc będziesz musiał rzucić wyjątek, jeśli ten kod trafi do produkcji.

 0
Author: Gerrie Schenck,
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-02-03 08:16:29