Patrzę na artykuł C # - Obiekt transferu danych na temat szeregowych DTO.
Artykuł zawiera ten fragment kodu:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
Reszta artykułu wygląda rozsądnie i rozsądnie (dla nooba), ale ten rzut próbny rzuca wyjątek WtfEx ... Czy to nie jest dokładnie równoznaczne z brakiem obsługi wyjątków?
Ergo:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
A może brakuje mi czegoś fundamentalnego w obsłudze błędów w C #? Jest prawie taki sam jak Java (bez zaznaczonych wyjątków), prawda? ... To znaczy, obaj dopracowali C ++.
Pytanie przepełnienia stosu Różnica między ponownym wyrzucaniem łapania bez parametrów a brakiem działania? wydaje się potwierdzać moją tezę, że try-catch-throw to brak możliwości.
EDYTOWAĆ:
Podsumowując, dla każdego, kto znajdzie ten wątek w przyszłości ...
NIE RÓB
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
Informacje dotyczące śledzenia stosu mogą być kluczowe dla zidentyfikowania pierwotnej przyczyny problemu!
ZROBIĆ
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
Złap bardziej szczegółowe wyjątki przed mniej szczegółowymi (tak jak Java).
Bibliografia:
źródło
Odpowiedzi:
Pierwszy; sposób, w jaki kod w artykule to robi, jest zły.
throw ex
zresetuje stos wywołań w wyjątku do punktu, w którym znajduje się ta instrukcja throw; utrata informacji o tym, gdzie faktycznie utworzono wyjątek.Po drugie, jeśli po prostu złapiesz i przerzucisz w ten sposób, nie widzę żadnej wartości dodanej, powyższy przykład kodu byłby tak samo dobry (lub, biorąc pod uwagę
throw ex
bit, nawet lepszy) bez try-catch.Są jednak przypadki, w których możesz chcieć złapać i ponownie zgłosić wyjątek. Logowanie może być jednym z nich:
źródło
ex
obiektu, nie ma potrzeby tworzenia go.throw ex
nie uruchamia ponownie śledzenia stosu.Nie rób tego
Utracisz informacje śledzenia stosu ...
Albo
LUB
Jednym z powodów, dla których możesz chcieć ponownie rzucić, jest to, że zajmujesz się różnymi wyjątkami, na przykład
źródło
C # (przed C # 6) nie obsługuje „odfiltrowanych wyjątków” CIL, co robi VB, więc w C # 1-5 jednym z powodów ponownego zgłoszenia wyjątku jest to, że nie masz wystarczającej ilości informacji w momencie catch () aby ustalić, czy rzeczywiście chcesz złapać wyjątek.
Na przykład w VB możesz to zrobić
... który nie obsługiwałby MyExceptions z różnymi wartościami ErrorCode. W wersji C # przed wersją 6 trzeba złapać i ponownie wyrzucić MyException, jeśli kod błędu nie wynosił 123:
Od wersji C # 6.0 możesz filtrować tak samo jak w VB:
źródło
Mój główny powód posiadania kodu, takiego jak:
jest tak, że mogę mieć punkt przerwania w catch, który ma instancję wyjątku. Często to robię podczas programowania / debugowania. Oczywiście kompilator ostrzega mnie przed wszystkimi nieużywanymi literami e, i najlepiej, jeśli powinny zostać usunięte przed kompilacją wydania.
Są jednak miłe podczas debugowania.
źródło
#if DEBUG
wokół ZARÓWNOtry {
i} catch () {...}
jest trochę niechlujny i, szczerze mówiąc, sprawia, że mam mdłości ... Preprocesor , ogólnie rzecz biorąc, nie jest moim przyjacielem.Prawidłowym powodem ponownego zgłaszania wyjątków może być to, że chcesz dodać informacje do wyjątku lub zawrzeć oryginalny wyjątek w jednym z twoich własnych działań:
źródło
Nie do końca, to nie to samo. Resetuje śledzenie stosu wyjątku. Chociaż zgadzam się, że jest to prawdopodobnie błąd, a zatem przykład złego kodu.
źródło
Nie chcesz rzucać ex - ponieważ spowoduje to utratę stosu wywołań. Zobacz Obsługa wyjątków (MSDN).
I tak, try ... catch nie robi nic pożytecznego (poza utratą stosu wywołań - tak naprawdę jest gorzej - chyba że z jakiegoś powodu nie chciałeś ujawniać tych informacji).
źródło
Rzeczą, o której ludzie nie wspominali, jest to, że chociaż języki .NET tak naprawdę nie robią właściwego rozróżnienia, to pytanie, czy należy podjąć działania w przypadku wystąpienia wyjątku i czy rozwiązać go , są tak naprawdę odrębnymi pytaniami. Istnieje wiele przypadków, w których należy podjąć działanie w oparciu o wyjątki, których nie ma nadziei na rozwiązanie, i są przypadki, w których wszystko, co jest konieczne do „rozwiązania” wyjątku, polega na rozwinięciu stosu do określonego punktu - nie są wymagane żadne dalsze działania .
Ze względu na powszechną mądrość, zgodnie z którą należy „łapać” rzeczy, które można „obsłużyć”, wiele kodów, które powinny być podejmowane w przypadku wystąpienia wyjątków, nie działa. Na przykład wiele kodów uzyska blokadę, ustawi tymczasowo „strzeżony” obiekt, który narusza jego niezmienniki, następnie wprowadzi obiekt do legalnego stanu, a następnie zwolni blokadę, zanim ktokolwiek będzie mógł zobaczyć obiekt. Jeśli wystąpi wyjątek, gdy obiekt znajduje się w niebezpiecznie niepoprawnym stanie, powszechną praktyką jest zwolnienie blokady, gdy obiekt nadal znajduje się w tym stanie. Znacznie lepszym wzorcem byłoby posiadanie wyjątku, który występuje, gdy obiekt znajduje się w „niebezpiecznym” stanie, wyraźnie unieważnia blokadę, więc każda kolejna próba jej zdobycia natychmiast się nie powiedzie.
W większości języków .NET jedynym sposobem na wykonanie akcji w oparciu o wyjątek jest kod
catch
(nawet jeśli wie, że wyjątek nie zostanie rozwiązany), wykonaj dane działanie, a następnie ponowniethrow
). Innym możliwym podejściem, jeśli kod nie dba o zgłoszony wyjątek, jest użycieok
flagi ztry/finally
blokiem; ustawok
flagę nafalse
przed blokiem itrue
przed wyjściem z bloku i przed wszystkimi,return
które znajdują się w bloku. Wewnątrzfinally
załóżmy, że jeśliok
nie jest ustawiony, musiał wystąpić wyjątek. Takie podejście jest semantycznie lepsze niż acatch
/throw
, ale jest brzydkie i trudniejsze do utrzymania niż powinno być.źródło
Może to być przydatne, gdy programujesz funkcje biblioteki lub biblioteki DLL.
Tej struktury przerzucania można użyć do celowego zresetowania stosu wywołań, aby zamiast widzieć wyjątek zgłoszony z pojedynczej funkcji wewnątrz funkcji, wyjątek otrzymujesz od samej funkcji.
Myślę, że jest to po prostu używane, aby zgłoszone wyjątki były czystsze i nie wchodziły w „korzenie” biblioteki.
źródło
Jednym z możliwych powodów wyłapania jest wyłączenie filtrowania wyjątków znajdujących się głębiej na stosie ( losowy stary link ). Ale oczywiście, jeśli taka była intencja, pojawiłby się tam komentarz.
źródło
Zależy to od tego, co robisz w bloku catch, i czy chcesz przekazać błąd do kodu wywołującego, czy nie.
Możesz powiedzieć,
Catch io.FileNotFoundExeption ex
a następnie użyć alternatywnej ścieżki do pliku lub innej, ale nadal włączać błąd.Robienie
Throw
zamiast zamiastThrow Ex
pozwala zachować ślad pełnego stosu. Throw ex ponownie uruchamia śledzenie stosu z instrukcji throw (mam nadzieję, że ma to sens).źródło
Podczas gdy wiele innych odpowiedzi dostarcza dobrych przykładów, dlaczego warto złapać powtórzenie wyjątku, wydaje się, że nikt nie wspomniał o scenariuszu „w końcu”.
Przykładem tego jest metoda, w której ustawiasz kursor (na przykład kursor oczekiwania), metoda ma kilka punktów wyjścia (np. If () return;) i chcesz upewnić się, że kursor został zresetowany na koniec metody.
Aby to zrobić, możesz owinąć cały kod w try / catch / wreszcie. Na koniec ustaw kursor z powrotem na prawy kursor. Aby nie zakopać żadnych ważnych wyjątków, wrzuć to do haczyka.
źródło
catch
to obowiązkowa częśćtry...finally
historii, czy też odgrywa rolę funkcjonalną w tym przykładzie? - Właśnie dwukrotnie sprawdziłem i jestem w stanie używaćtry {} finally {}
bez bloku catch.W przykładzie w opublikowanym kodzie nie ma sensu wychwytywać wyjątku, ponieważ nic nie zrobiono w przypadku tego, że jest on ponownie zgłaszany, w rzeczywistości powoduje więcej szkody niż pożytku, ponieważ stos wywołań zostaje utracony .
Złapałbyś jednak wyjątek, aby wykonać logikę (na przykład zamknięcie połączenia sql blokady pliku lub po prostu rejestrowania) w przypadku wyjątku i wrzucić go z powrotem do kodu wywołującego, aby sobie z tym poradzić. Byłoby to bardziej powszechne w warstwie biznesowej niż w kodzie frontonu, ponieważ koder implementujący warstwę biznesową może obsłużyć wyjątek.
Aby powtórzyć iterację: Nie ma sensu łapać wyjątku w opublikowanym przykładzie. NIE rób tak!
źródło
Przykro nam, ale wiele przykładów „ulepszonego projektu” nadal śmierdzi okropnie lub może być bardzo mylące. Próbowanie {} catch {log; throw} jest całkowicie bezcelowe. Rejestrowanie wyjątków powinno odbywać się w centralnym miejscu wewnątrz aplikacji. wyjątki i tak burzą stos śledzenia, dlaczego nie zarejestrować ich gdzieś w pobliżu granic systemu?
Należy zachować ostrożność, gdy serializujesz kontekst (np. DTO w jednym podanym przykładzie) tylko w komunikacie dziennika. Może łatwo zawierać poufne informacje, które mogą nie być dostępne dla wszystkich osób, które mogą uzyskać dostęp do plików dziennika. A jeśli nie dodasz żadnych nowych informacji do wyjątku, naprawdę nie widzę sensu owijania wyjątków. Dobra stara Java ma w tym sens, wymaga od osoby dzwoniącej, jakich wyjątków należy się spodziewać po wywołaniu kodu. Ponieważ nie masz tego w .NET, owijanie nie przynosi żadnego efektu w co najmniej 80% przypadków, które widziałem.
źródło
Oprócz tego, co powiedzieli inni, zobacz moją odpowiedź na powiązane pytanie, które pokazuje, że łapanie i ponowne rzucanie nie jest opcją (jest w VB, ale część kodu można wywołać w C # z VB).
źródło
Większość odpowiedzi mówi o powtórzeniu scenariusza.
Zamiast wpisywać go w kodzie, rozważ użycie AOP, w szczególności Postsharp.Diagnostic.Toolkit z OnExceptionOptions IncludeParameterValue i IncludeThisArgument
źródło
Ponowne przeglądanie wyjątków za pośrednictwem
throw
jest przydatne, gdy nie masz określonego kodu do obsługi bieżących wyjątków lub w przypadkach, gdy masz logikę do obsługi określonych przypadków błędów, ale chcesz pominąć wszystkie inne.Przykład:
Istnieje jednak również inny sposób , wykorzystując klauzule warunkowe w blokach catch:
źródło