С нашим общедоступным SDK мы стремимся давать очень информативные сообщения о том, почему возникает исключение. Например:
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);
}
Однако, это имеет тенденцию загромождать поток кода, так как он имеет тенденцию уделять большое внимание сообщениям об ошибках, а не тому, что делает код.
Коллега начал рефакторинг некоторых исключений, бросая что-то вроде этого:
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);
}
Что облегчает понимание логики кода, но добавляет множество дополнительных методов для обработки ошибок.
Каковы другие способы избежать проблемы "логики беспорядка длинных сообщений об исключениях"? Я в первую очередь спрашиваю об идиоматическом C # / .NET, но как другие языки управляют им, также полезно.
[Редактировать]
Было бы неплохо иметь плюсы и минусы каждого подхода.
c#
.net
clean-code
exception-handling
FriendlyGuy
источник
источник
Exception.Data
свойства, «выборочной» перехвата исключений, перехвата кода и добавления собственного контекста вместе с перехваченным стеком вызовов - все это вносит информацию, которая должна позволять гораздо меньше многословных сообщений. Наконец,System.Reflection.MethodBase
выглядит многообещающим для предоставления деталей для перехода к вашему методу "создания исключений".Exception.Data
. Акцент должен быть захват телеметрии. Рефакторинг здесь хорошо, но он не решает проблему.Ответы:
Почему бы не иметь специализированные классы исключений?
Это переносит форматирование и детали к самому исключению и оставляет основной класс незагроможденным.
источник
Кажется, что Microsoft (просматривая исходный код .NET) иногда использует строки ресурсов / среды. Например,
ParseDecimal
:Плюсы:
Минусы:
источник
Для общедоступного сценария SDK я настоятельно рекомендую использовать Microsoft Code Contracts, поскольку они предоставляют информативные ошибки, статические проверки, а также вы можете создавать документацию для добавления в документы XML и файлы справки, созданные в Sandcastle . Поддерживается во всех платных версиях Visual Studio.
Дополнительным преимуществом является то, что, если ваши клиенты используют C #, они могут использовать ваши ссылочные сборки контракта кода для выявления потенциальных проблем даже до запуска своего кода.
Полная документация по кодовым контрактам находится здесь .
источник
Техника, которую я использую, состоит в том, чтобы объединить и передать проверку и использование функции полезности.
Единственное наиболее важное преимущество заключается в том, что оно сводится к одной строке в бизнес-логике .
Могу поспорить, что вы не сможете добиться большего успеха, если не сможете уменьшить его еще больше - исключить все проверки аргументов и средства защиты состояния объекта из бизнес-логики, сохраняя только эксплуатационные исключительные условия.
Конечно, есть способы сделать это - строго типизированный язык, дизайн «недопустимые объекты запрещены в любое время», проектирование по контракту и т. Д.
Пример:
источник
[примечание] Я скопировал это из вопроса в ответ, если есть комментарии по этому поводу.
Переместите каждое исключение в метод класса, принимая любые аргументы, которые требуют форматирования.
Вложите все методы исключения в регион и поместите их в конец класса.
Плюсы:
Минусы:
источник
#region #endregion
(что по умолчанию скрывает их от IDE), и, если они применимы для разных классов, поместите их вinternal static ValidationUtility
класс. Кстати, никогда не жалуйтесь на длинные имена перед программистом C #.Если вы можете избежать более общих ошибок, вы можете написать для вас публичную статическую обобщенную функцию приведения, которая выводит тип источника:
Возможны варианты (подумайте
SdkHelper.RequireNotNull()
), которые только проверяют требования к входам и бросают, если они терпят неудачу, но в этом примере объединение приведения с получением результата является самодокументированным и компактным.Если вы работаете в .net 4.5, есть способы заставить компилятор вставить имя текущего метода / файла в качестве параметра метода (см. CallerMemberAttibute ). Но для SDK вы, вероятно, не можете требовать от своих клиентов перехода на 4.5.
источник
Что нам нравится делать для ошибок бизнес-логики (не обязательно ошибок аргументов и т. Д.), Так это иметь единственное перечисление, которое определяет все потенциальные типы ошибок:
Эти
[Display(Name = "...")]
атрибуты определяют ключ в ресурсе файлов , которые будут использоваться для перевода сообщений об ошибках.Кроме того, этот файл можно использовать в качестве отправной точки для поиска всех случаев, когда в вашем коде генерируется ошибка определенного типа.
Проверка бизнес-правил может быть делегирована специализированным классам валидаторов, которые выдают списки нарушенных бизнес-правил.
Затем мы используем пользовательский тип исключения для транспортировки нарушенных правил:
Вызовы сервисной службы заключены в общий код обработки ошибок, который переводит нарушенное правило Busines в удобочитаемое сообщение об ошибке:
Вот
ToTranslatedString()
метод расширения,enum
который может считывать ключи ресурсов из[Display]
атрибутов и использовать ихResourceManager
для перевода. Значение для соответствующего ключа ресурса может содержать заполнители дляstring.Format
, которые соответствуют предоставленнымMessageParameters
. Пример записи в файле resx:Пример использования:
При таком подходе вы можете отделить генерацию сообщения об ошибке от генерации ошибки, без необходимости вводить новый класс исключений для каждого нового типа ошибки. Полезно, если разные интерфейсы должны отображать разные сообщения, если отображаемое сообщение должно зависеть от языка и / или роли пользователя и т. Д.
источник
Я бы использовал охранные предложения, как в этом примере, чтобы проверки параметров можно было использовать повторно.
Кроме того, вы можете использовать шаблон компоновщика, чтобы можно было объединить несколько проверок. Это будет выглядеть как беглые утверждения .
источник