Як слід надати додаткову інформацію про виняток?


20

Щоразу, коли мені потрібно надати додаткову інформацію про виняток, мені цікаво, який спосіб насправді є правильним .


Заради цього питання я написав приклад. Припустимо, існує клас, в якому ми хочемо оновити Abbreviationвластивість. З точки зору SOLID, це може бути не ідеальним, але навіть якби ми пройшли метод робітника через DI з якоюсь службою, відбудеться та сама ситуація - виняток виникає, тому що для цього немає контексту. Повернутися до прикладу ...

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

Потім є деякі екземпляри класу і цикл, де викликається метод-робочий. Це може кинути своє 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
}

Питання: як додати Personабо його Id(або що-небудь ще)?


Я знаю наступні три методи:


1 - Використовуйте Dataмайно

Плюси:

  • легко встановити додаткову інформацію
  • не вимагає створення ще більше винятків
  • не потребує додаткових try/catch

Мінуси:

  • не може бути легко інтегрована в Message
  • реєстратори ігнорують це поле і не скидають його
  • Потрібні ключі та кастинг, оскільки значення є object
  • не незмінна

Приклад:

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 - Використовуйте власні властивості

Плюси:

  • схожий на Dataмайно, але сильно набраний
  • простіше інтегруватися в Message

Мінуси:

  • вимагає спеціальних винятків
  • реєстратор проігнорує їх
  • не незмінна

Приклад:

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 - Оберніть виняток іншим винятком

Плюси:

  • Message можуть бути відформатовані передбачуваним способом
  • реєстратори скидають внутрішні винятки
  • непорушний

Мінуси:

  • вимагає додаткових try/catch
  • збільшує гніздування
  • збільшує глибину експепцій

Приклад:

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...
}

  • Чи є інші візерунки?
  • Чи є кращі зразки?
  • Чи можете ви запропонувати найкращі практики для будь-якого з них?

Не знайомий з винятками в C #, але, як правило, я очікую, що екземпляр Person буде дійсним, коли виняток буде викинуто. Ви пробували це?
Джон Куракліс

1
@JohnKouraklis - це не про що йдеться ;-) Це просто надзвичайно простий приклад продемонструвати, що я маю на увазі під додатковою інформацією. Якби я розмістив тут цілий фреймворк, де мутліпл-методи можуть запускати винятки та змішувати рівні контекстної інформації, ніхто, мабуть, не читав би цього, і мені було дуже важко пояснити це.
t3chb0t

@JohnKouraklis Я щойно склав це для демонстраційних цілей.
t3chb0t

@ t3chb0t Я думаю, ви тут відповіли на власне запитання. Подумайте про те, як перейти 1, 2 і 3 до відповіді та налаштувати своє запитання, щоб не просити мене вибрати стиль, виходячи з моєї думки.
candied_orange

Що не так із спеціальними винятками? Зроблено належним чином, вони є частиною мови вашого домену та допомагають досягти абстрагування від деталей щодо впровадження.
RubberDuck

Відповіді:


6

Data FTW .

Ваш "контраст":

  • "не може бути легко інтегрований у повідомлення"

-> Для ваших типів винятків, вона повинна бути досить легко перевизначити Messageтак , що вона робить інкорпорувати Data.. хоча я тільки хотів би розглянути це , якщо Dataце повідомлення .

  • "реєстратори ігнорують це поле і не скидають його"

Гуглінг для Nlog як приклад дає :

Відображення макета винятку

(...)

формат - Формат виводу. Повинно бути коми список властивостей виключення: Message, Type, ShortType, ToString, Method, StackTraceі Data. Це значення параметра нечутливе до регістру. За замовчуванням:message

Тож здається, це легко налаштувати.

  • потрібні ключі та кастинг, оскільки значення є об'єктом

Так? Просто скиньте туди об’єкти і переконайтесь, що вони мають придатний ToString()метод.

Крім того, я не дуже бачу проблем з клавішами. Просто використовуйте трохи легкої унікальності, і ви добре.


Відмова: Це те, що я міг відразу побачити з питання, і що я гугл вгору за Data15 хвилин. Я подумав, що це корисно, тому я висловив це як відповідь, але я ніколи не використовував Dataсебе, тому, можливо, може бути, що запитуючий тут знає про це більше, ніж я.


Я прийшов до висновку, що щодо корисного виключення є лише дві речі, його ім’я та повідомлення. Все інше - це просто марний шум, який можна і слід ігнорувати, оскільки на нього просто надто крихко покладатися.
t3chb0t

2

Чому ви кидаєте винятки? Щоб їх спіймали та обробляли.

Як працює ловуючий код, як поводитися з винятком? Використання властивостей, які ви визначаєте на об’єкті Exception.

Ніколи не використовуйте властивість Message для ідентифікації винятку і не надавати "інформацію", на яку повинен покладатися будь-який потенційний обробник. Це просто занадто мінливий і ненадійний.

Я ніколи раніше не використовував властивість "Дані", але для мене це звучить занадто загально.

Якщо ви не створили багато класів винятку, кожен з яких визначає конкретний винятковий випадок, як ви дізнаєтесь, коли ви ловите виняток, що являє собою "Дані"? (Див. Попередній коментар про "Повідомлення").


1
Я б сказав, Dataмарна в обробці, але цінна для ведення журналів, щоб уникнути Messageформатизації пекла.
Мартін Ба

-1

Мені подобається твій третій приклад, однак є інший спосіб, за допомогою якого можна кодувати, щоб усунути більшість ваших "кон".

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);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.