Каждый раз, когда мне нужно предоставить дополнительную информацию об исключении, я задаюсь вопросом, какой способ действительно является правильным .
Ради этого вопроса я написал пример. Давайте предположим, что есть класс, где мы хотим обновить 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...
}
- Есть ли другие модели?
- Есть ли лучшие образцы?
- Можете ли вы предложить лучшие практики для любого из них?
источник
Ответы:
Data
FTW .Ваш "контра":
-> Для ваших типов исключений, она должна быть достаточно легко переопределить
Message
так , что она делает инкорпорироватьData
.. хотя я только хотел бы рассмотреть это , еслиData
это сообщение .Поиск в Google для Nlog в качестве примера дает :
Кажется, это легко настраивается.
А? Просто добавьте туда объекты и убедитесь, что у них есть подходящий
ToString()
метод.Кроме того, я не вижу никаких проблем с ключами. Просто используйте небольшую уникальность, и все хорошо.
Отказ от ответственности: это то, что я сразу понял из вопроса и на что я погуглил за
Data
15 минут. Я подумал, что это немного полезно, поэтому я изложил это как ответ, но я никогда не использовалData
себя, так что вполне возможно, что спрашивающий здесь знает об этом гораздо больше, чем я.источник
Почему вы бросаете исключения? Чтобы их поймали и обработали.
Как работает ловящий код, как обрабатывать исключение? Используя свойства, которые вы определяете для объекта Exception.
Никогда не используйте свойство Message, чтобы идентифицировать исключение или предоставить «информацию», на которую должен опираться любой потенциальный обработчик. Это просто слишком изменчиво и ненадежно.
Я никогда раньше не использовал свойство «Данные», но оно звучит слишком обобщенно для меня.
Если вы не создадите много классов исключений, каждый из которых идентифицирует конкретный исключительный случай, как вы узнаете, когда поймаете исключение, которое представляет «Данные»? (См. Предыдущий комментарий о «Сообщение»).
источник
Data
это бесполезно для обработки, но полезно для регистрации, чтобы избежатьMessage
форматирования ада.Мне нравится ваш третий пример, однако есть и другой способ, которым он может быть закодирован для устранения большинства ваших "минусов".
источник