Dzięki naszemu publicznemu pakietowi SDK chcemy przekazywać bardzo pouczające informacje o przyczynach wyjątku. Na przykład:
if (interfaceInstance == null)
{
string errMsg = string.Format(
"Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
ParameterInfo.Name,
ParameterInfo.ParameterType,
typeof(IParameter)
);
throw new InvalidOperationException(errMsg);
}
Ma to jednak tendencję do zaśmiecania przepływu kodu, ponieważ koncentruje się raczej na komunikatach o błędach niż na tym, co robi kod.
Kolega zaczął refaktoryzować niektóre wyjątki, zgłaszając coś takiego:
if (interfaceInstance == null)
throw EmptyConstructor();
...
private Exception EmptyConstructor()
{
string errMsg = string.Format(
"Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
ParameterInfo.Name,
ParameterInfo.ParameterType,
typeof(IParameter)
);
return new InvalidOperationException(errMsg);
}
Co sprawia, że logika kodu jest łatwiejsza do zrozumienia, ale dodaje wiele dodatkowych metod obsługi błędów.
Jakie są inne sposoby na uniknięcie problemu logiki zakłóceń długich wyjątków? Pytam przede wszystkim o idiomatic C # / .NET, ale pomocne są również sposoby zarządzania innymi językami.
[Edytować]
Byłoby miło mieć zalety i wady każdego podejścia.
c#
.net
clean-code
exception-handling
FriendlyGuy
źródło
źródło
Exception.Data
właściwości, „wybrednego” wychwytywania wyjątków, wywoływania kodu i dodawania własnego kontekstu, wraz z przechwyconym stosem wywołań, wszystko to zapewnia informacje, które powinny pozwolić na znacznie mniej pełne komunikaty. WreszcieSystem.Reflection.MethodBase
wygląda obiecująco, podając szczegółowe informacje na temat metody „wyjątkowej konstrukcji”.Exception.Data
. Nacisk należy położyć na przechwytywanie danych telemetrycznych. Refaktoryzacja jest w porządku, ale pomija problem.Odpowiedzi:
Dlaczego nie mieć specjalistycznych klas wyjątków?
To przesuwa formatowanie i szczegóły do samego wyjątku i pozostawia główną klasę w porządku.
źródło
Wydaje się, że Microsoft (patrząc na źródło .NET) czasami używa ciągów zasobów / środowiska. Na przykład
ParseDecimal
:Plusy:
Cons:
źródło
W przypadku publicznego scenariusza SDK zdecydowanie rozważam użycie kontraktów Microsoft Code, ponieważ dostarczają one błędów informacyjnych, kontroli statycznych, a także można generować dokumentację do dodania do dokumentów XML i plików pomocy generowanych przez Sandcastle . Jest obsługiwany we wszystkich płatnych wersjach programu Visual Studio.
Dodatkową zaletą jest to, że jeśli Twoi klienci używają C #, mogą wykorzystać zestawy referencyjne umów kodu, aby wykryć potencjalne problemy nawet przed uruchomieniem kodu.
Pełna dokumentacja dotycząca umów kodowych znajduje się tutaj .
źródło
Techniką, której używam, jest łączenie i outsourcing walidacji i rzucanie całkowicie funkcji użytecznej.
Najważniejszą zaletą jest to, że jest zredukowana do logiki biznesowej .
Założę się, że nie da się tego zrobić lepiej, jeśli nie można go jeszcze bardziej ograniczyć - aby wyeliminować wszystkie sprawdzania poprawności argumentów i zabezpieczenia obiektów przed stanem z logiki biznesowej, zachowując jedynie wyjątkowe warunki operacyjne.
Istnieją oczywiście sposoby na to - silnie napisany język, projektowanie „niedozwolonych obiektów w dowolnym momencie”, projektowanie na podstawie umowy itp.
Przykład:
źródło
[uwaga] Skopiowałem to pytanie z odpowiedzi na wypadek, gdyby były na ten temat komentarze.
Przenieś każdy wyjątek do metody klasy, biorąc wszelkie argumenty wymagające formatowania.
Umieść wszystkie metody wyjątków w regionie i umieść je na końcu klasy.
Plusy:
Cons:
źródło
#region #endregion
(co powoduje, że są one domyślnie ukryte przed IDE), a jeśli mają zastosowanie w różnych klasach, należy umieścić je winternal static ValidationUtility
klasie. Przy okazji, nigdy nie narzekaj na długie nazwy identyfikatorów przed programistą C #.Jeśli możesz uniknąć nieco bardziej ogólnych błędów, możesz napisać publiczną statyczną ogólną funkcję rzutowania, która podaje typ źródła:
Możliwe są różne warianty (pomyśl
SdkHelper.RequireNotNull()
), które sprawdzają tylko wymagania dotyczące danych wejściowych i rzucają, jeśli zawiodą, ale w tym przykładzie połączenie obsady z uzyskaniem wyniku jest samoudokumentujące i kompaktowe.Jeśli korzystasz z .net 4.5, istnieją sposoby, aby kompilator wstawił nazwę bieżącej metody / pliku jako parametr metody (patrz CallerMemberAttibute ). Ale w przypadku zestawu SDK prawdopodobnie nie możesz wymagać od klientów przejścia na wersję 4.5.
źródło
To, co lubimy robić w przypadku błędów logiki biznesowej (niekoniecznie błędów argumentów itp.), To mieć jeden wyliczenie, które definiuje wszystkie potencjalne typy błędów:
Te
[Display(Name = "...")]
atrybuty określają klucz w zasobie pliki mają być wykorzystane do tłumaczenia komunikatów o błędach.Ponadto ten plik może służyć jako punkt początkowy do znalezienia wszystkich przypadków, w których w kodzie generowany jest określony rodzaj błędu.
Sprawdzanie reguł biznesowych można przekazać do wyspecjalizowanych klas Validatora, które dostarczają listy naruszonych reguł biznesowych.
Następnie używamy niestandardowego typu wyjątku do transportu naruszonych reguł:
Wywołania usługi wewnętrznej bazy danych są pakowane w ogólny kod obsługi błędów, który tłumaczy naruszoną zasadę Busines na czytelny dla użytkownika komunikat o błędzie:
Oto
ToTranslatedString()
metoda rozszerzenia,enum
która może odczytać klucze zasobów z[Display]
atrybutów i użyćResourceManager
do przetłumaczenia tych kluczy. Wartość odpowiedniego klucza zasobu może zawierać symbole zastępczestring.Format
odpowiadające podanemuMessageParameters
. Przykład wpisu w pliku resx:Przykładowe użycie:
Dzięki takiemu podejściu można oddzielić generowanie komunikatu o błędzie od wygenerowania błędu, bez potrzeby wprowadzania nowej klasy wyjątków dla każdego nowego typu błędu. Przydatne, jeśli różne nakładki powinny wyświetlać różne komunikaty, jeśli wyświetlany komunikat powinien zależeć od języka użytkownika i / lub roli itp.
źródło
Użyłbym klauzul ochronnych, jak w tym przykładzie, aby można było ponownie użyć sprawdzania parametrów.
Ponadto można użyć wzorca konstruktora, aby połączyć więcej niż jedną weryfikację. Będzie to wyglądać trochę jak płynne twierdzenia .
źródło