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) ?
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!
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ć! ;-)
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ż:
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.
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ę.
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.
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.
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.
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.
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