Jak mam podać dodatkowe informacje na temat wyjątku?

20

Za każdym razem, gdy muszę podać dodatkowe informacje na temat wyjątku, zastanawiam się, który sposób jest właściwy .


Na potrzeby tego pytania napisałem przykład. Załóżmy, że istnieje klasa, w której chcemy zaktualizować Abbreviationwłaściwość. Z SOLIDOWEGO punktu widzenia może to nie być idealne, ale nawet gdybyśmy przeszli metodę roboczą za pośrednictwem DI z pewną usługą, wystąpiłaby taka sama sytuacja - występuje wyjątek i nie ma w tym kontekście kontekstu. Powrót do przykładu ...

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Abbreviation { get; set; }
}

Następnie są pewne instancje klasy i pętla, w której wywoływana jest metoda robotnicza. Może rzucić StringTooShortException.

var persons =
{
    new Person { Id = 1, Name = "Fo" },
    new Person { Id = 2, Name = "Barbaz" },
}

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // ?
        }
    }
    // throw AggregateException...
}

public IEnumerable<string> GenerateAbbreviation(string value)
{
    if (value.Length < 5)
    {
        throw new StringTooShortException(value);
    }

    // generate abbreviation
}

Pytanie brzmi: jak dodać Personlub jego Id(lub cokolwiek innego)?


Znam następujące trzy techniki:


1 - Użyj Datanieruchomości

Plusy:

  • łatwo ustawić dodatkowe informacje
  • nie wymaga tworzenia jeszcze większej liczby wyjątków
  • nie wymaga dodatkowych try/catch

Cons:

  • nie może być łatwo zintegrowany z Message
  • loggery ignorują to pole i nie zrzucają go
  • wymaga kluczy i rzutowania, ponieważ wartości są object
  • niezmienne

Przykład:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            ex.Data["PersonId"] = person.Id;
            // collect ex
        }
    }
    // throw AggregateException...
}

2 - Użyj niestandardowych właściwości

Plusy:

  • podobny do Datawłaściwości, ale mocno napisany
  • łatwiejsze do zintegrowania z Message

Cons:

  • wymaga niestandardowych wyjątków
  • logger je zignoruje
  • niezmienne

Przykład:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // not suitable for this exception because 
            // it doesn't have anything in common with the Person
        }
    }
    // throw AggregateException...
}

3 - Zawiń wyjątek innym

Plusy:

  • Message można sformatować w przewidywalny sposób
  • programy rejestrujące zrzucą wewnętrzne wyjątki
  • niezmienny

Cons:

  • wymaga dodatkowych try/catch
  • zwiększa zagnieżdżanie
  • zwiększa głębokość wyjątków

Przykład:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            try
            {
                person.Abbreviation = GenerateAbbreviation(person.Name);
            }
            catch(Exception ex)
            {
                throw new InvalidPersonDataException(person.Id, ex);
            }
        }
        catch(Exception ex)
        {
            // collect ex
        }
    }
    // throw AggregateException...
}

  • Czy są jakieś inne wzorce?
  • Czy są lepsze wzory?
  • Czy możesz zasugerować najlepsze praktyki dla któregokolwiek / wszystkich?
t3chb0t
źródło
Nie jestem zaznajomiony z wyjątkami w języku C #, ale normalnie oczekiwałbym, że instancja Person nadal będzie ważna, gdy zostanie zgłoszony wyjątek. Próbowałeś tego?
John Kouraklis,
1
@JohnKouraklis nie o to chodzi ;-) To bardzo prosty przykład, aby zademonstrować, co rozumiem przez dodatkowe informacje. Gdybym zamieścił tutaj całą strukturę, w której metody wielokrotnego użytku mogą generować wyjątki i różne poziomy informacji kontekstowych, nikt prawdopodobnie tego nie przeczytałby i bardzo trudno mi było to wyjaśnić.
t3chb0t,
@JohnKouraklis Właśnie to zrobiłem w celach demonstracyjnych.
t3chb0t,
@ t3chb0t Myślę, że odpowiedziałeś na własne pytanie tutaj. Zastanów się nad przeniesieniem 1, 2 i 3 na odpowiedź i dostosowaniem swojego pytania, aby nie wymagało ode mnie wyboru stylu na podstawie mojej opinii.
candied_orange
Co jest złego w niestandardowych wyjątkach? Wykonane prawidłowo, są częścią języka domeny i pomagają uzyskać abstrakcję poza szczegółami implementacji.
RubberDuck,

Odpowiedzi:

6

Data FTW .

Twój „contra”:

  • „nie można łatwo zintegrować z wiadomością”

-> Na swoim rodzaju wyjątku, powinien on być na tyle łatwe do zastąpienia Message, tak aby nie włączać Data.. chociaż ja tylko uznałby to jeżeli Datajest wiadomość .

  • „rejestrujący ignorują to pole i nie zrzucają go”

Googling dla Nlog jako przykład daje :

Mechanizm renderujący układ wyjątków

(...)

format - Format wyniku. Musi być oddzielony przecinkami właściwości wyjątek: Message, Type, ShortType, ToString, Method, StackTracei Data. Ta wartość parametru nie uwzględnia wielkości liter. Domyślna:message

Wygląda na to, że łatwo to skonfigurować.

  • wymaga kluczy i rzutowania, ponieważ wartości są obiektowe

Co? Po prostu zrzuć znajdujące się tam obiekty i upewnij się, że mają one użyteczną ToString()metodę.

Poza tym nie widzę żadnych problemów z kluczami. Po prostu użyj łagodnej wyjątkowości i jesteś dobry.


Zrzeczenie się: To właśnie od razu mogłem zobaczyć z pytania i na co wpadłem Dataw ciągu 15 minut. Pomyślałem, że jest to raczej pomocne, dlatego udzieliłem odpowiedzi, ale nigdy się nie wykorzystałem Data, więc może się zdarzyć, że pytający o tym wie o wiele więcej niż ja.

Martin Ba
źródło
Doszedłem do wniosku, że są tylko dwie rzeczy dotyczące wyjątku, które są przydatne, jego nazwa i przesłanie. Cała reszta to po prostu bezużyteczny hałas, który można i należy zignorować, ponieważ jest po prostu zbyt delikatny.
t3chb0t
2

Dlaczego rzucasz wyjątki? Aby je złapać i poradzić sobie.

Jak działa kod przechwytujący, jak obsługiwać wyjątek? Korzystanie z właściwości zdefiniowanych w obiekcie wyjątku.

Nigdy nie używaj właściwości Message do identyfikowania wyjątku ani do dostarczania „informacji”, na których powinien polegać każdy potencjalny moduł obsługi. Jest po prostu zbyt niestabilny i zawodny.

Nigdy wcześniej nie korzystałem z właściwości „Data”, ale wydaje mi się to zbyt ogólne.

Jeśli nie utworzysz wielu klas wyjątków, z których każda identyfikuje konkretny wyjątkowy przypadek, skąd wiesz, kiedy złapiesz wyjątek, co oznacza „Dane”? (Zobacz poprzedni komentarz do „Wiadomości”).

Phill W.
źródło
1
Powiedziałbym, że Datajest bezużyteczny do obsługi, ale cenny do logowania, aby uniknąć Messageformatowania piekła.
Martin Ba
-1

Podoba mi się twój trzeci przykład, jednak istnieje inny sposób, w jaki można go zakodować, aby wyeliminować większość twoich „oszustw”.

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    var exceptions = new List<InvalidPersonDataException>();

    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            exceptions.Add(new InvalidPersonDataException(person.Id, ex));
        }
    }

    if (exceptions.Any())
    {
        throw new AggregateException(exceptions);
    }
}
krillgar
źródło