Технически дорого создавать новые исключения, однако я не буду спорить об этом, так как «дорогостоящий» относителен - если вы выбрасываете 100 таких исключений в минуту, вы, скорее всего, не увидите стоимость; если вы генерируете 1000 таких исключений в секунду, вы очень хорошо можете увидеть снижение производительности (следовательно, обсуждать здесь не стоит - производительность - это ваш вызов).
Я думаю, мне нужно спросить, почему этот подход используется. Действительно ли вы можете добавить значимую информацию об исключениях на уровне на каждом уровне, где может быть сгенерировано исключение, и, если это так, также верно, что информация будет иметь вид:
- Что-то, что вы на самом деле хотите поделиться со своим пользователем?
- Что-то, что ваш пользователь сможет интерпретировать, понимать и использовать ?
- Написано таким образом, чтобы оно не мешало последующему повторному использованию низкоуровневых компонентов, полезность которых может быть неизвестна при их написании?
Я спрашиваю об обмене информацией с вашим пользователем, потому что, в вашем примере, ваш искусственный стек начинается с информирования пользователя о том, что произошла ошибка при аутентификации в базе данных. Для потенциального хакера это хорошая информация, которая раскрывает информацию о том, что делала операция.
Что касается возврата целого стека пользовательских исключений, я не думаю, что это будет полезно большинству (честных) пользователей. Например, если у меня возникают проблемы с получением списка имен клиентов, это поможет мне (как пользователю) узнать, что была проблема с аутентификацией в базе данных? Если вы не используете встроенную аутентификацию, и у каждого из ваших пользователей есть учетная запись и возможность связаться с системным администратором, чтобы выяснить, почему их учетной записи не хватает привилегий, вероятно, нет.
Сначала я бы решил, действительно ли существует семантическая разница между генерируемым исключением Framework и сообщением об исключении, которое вы хотели бы предоставить пользователю. Если это так, то используйте нестандартное исключение на самом низком уровне («ошибка входа в систему» в вашем примере). Следующие шаги, вплоть до фактического представления исключения, на самом деле не требуют каких-либо пользовательских исключений. Интересующее вас исключение уже было сгенерировано (вход в систему не удался) - продолжение переноса этого сообщения на каждом уровне стека вызовов не служит никакой реальной цели, кроме раскрытия вашего стека вызовов вашим пользователям. Для этих «средних» шагов, при условии наличия блоков try / catch, простая стратегия «log and throw» подойдет.
Правда, у этой стратегии есть еще один потенциальный недостаток: она налагает на разработчика ответственность за поддержание внедренного стандарта пользовательских исключений. Поскольку вы не можете знать каждую перестановку иерархии вызовов при написании низкоуровневых типов (их «клиенты», возможно, даже еще не были написаны), маловероятно, что все разработчики - или даже один разработчик - не забудут обернуть и настроить любое состояние ошибки в каждом блоке кода.
Вместо того, чтобы работать снизу вверх, я обычно беспокоюсь об отображении сгенерированных исключений как можно более поздно в процессе (т. Е. Как можно ближе к «вершине» стека вызовов). Обычно я не пытаюсь заменить какие-либо сообщения в исключениях, выдаваемых на низком уровне моих приложений, особенно потому, что использование этих низкоуровневых членов имеет тенденцию становиться все более абстрактными по мере углубления вызова. Я склонен отлавливать и регистрировать исключения на бизнес-уровне и ниже, а затем иметь дело с отображением их понятным образом на уровне представления.
Вот несколько приличных статей о лучших методах обработки исключений:
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx
http://aspalliance.com/1119
Боже, я получил несколько ... извинений заранее.