Использование класса Exception в c # - PullRequest
5 голосов
/ 21 сентября 2008

Ошибки, возникающие глубоко на уровне доступа к данным или даже выше (например, в операциях ADO.net), редко имеют смысл для конечного пользователя. Простое всплытие этих ошибок до пользовательского интерфейса и их отображение обычно не принесет ничего, кроме разочарования для конечного пользователя.

Недавно я использовал базовую технику для сообщения об ошибках, такую ​​как эта, при которой я улавливаю ошибку и, по крайней мере, добавляю некоторый удобный текст, чтобы, по крайней мере, конечный пользователь понимал, что не удалось.

Чтобы сделать это, я ловлю исключение в каждой конкретной функции (скажем, например, функцию выборки на уровне доступа к данным), а затем выдаю новую ошибку с удобным для пользователя текстом о функции, которая завершилась ошибкой и, вероятно, вызывает, но затем встраивание исходного исключения в новое исключение в качестве «внутреннего исключения» этого нового исключения.

Это может произойти на каждом уровне, если необходимо, каждый потребитель функции более низкого уровня добавляет свой собственный контекст к сообщению об ошибке, так что то, что достигает пользовательского интерфейса, становится все более удобным для пользователя сообщением об ошибке.

Как только ошибка достигает пользовательского интерфейса - при необходимости - она ​​может перебирать вложенные исключения, чтобы отобразить сообщение об ошибке, которое сначала сообщает пользователю, какая операция завершилась неудачно, но также предоставляет немного технической информации о том, что на самом деле пошло не так .

например.

"Список имен ваших клиентов Запрошенный не может быть отображен. "

"Получение списка клиентов Вы запросили сбой из-за ошибки в базе данных. "

"Произошла ошибка при подключении к база данных при получении списка клиенты "

"Ошибка входа пользователя xx"

У меня такой вопрос: это ужасно неэффективно (все эти вложенные исключения)? Я подозреваю, что это не лучшая практика, так что я должен делать, чтобы достичь того же самого - или я действительно должен пытаться достичь чего-то лучшего?

Ответы [ 6 ]

9 голосов
/ 21 сентября 2008

Это просто немного ужасно.

Если вы показываете ошибку конечному пользователю, предполагается, что пользователь сможет ее исправить. В «Списке имен клиентов ваш запрос не может быть отображен». случай, ваш пользователь будет просто думать "и что?" Во всех этих случаях просто отобразите сообщение «что-то плохое случилось». Вам даже не нужно перехватывать эти исключения, когда что-то идет не так, пусть какой-то глобальный метод (например, application_error) обрабатывает это и отображает общее сообщение. Когда вы или ваш пользователь можете что-то сделать с ошибкой, поймайте ее и сделайте что-нибудь или оповестите пользователя.

Но вы захотите регистрировать каждую ошибку, которую вы не обрабатываете.

Кстати, отображение информации об ошибках может привести к уязвимостям безопасности. Чем меньше злоумышленники знают о вашей системе, тем меньше вероятность, что они найдут способы ее взлома (помните такие сообщения, как «Синтаксическая ошибка в выражении sql: выберите * У пользователей, где имя пользователя =« a »; база данных drp; -» .. . "Ожидается: вместо" drp "будет выброшено" drop ". Такие сайты больше не создаются).

8 голосов
/ 21 сентября 2008

Технически дорого создавать новые исключения, однако я не буду спорить об этом, так как «дорогостоящий» относителен - если вы выбрасываете 100 таких исключений в минуту, вы, скорее всего, не увидите стоимость; если вы генерируете 1000 таких исключений в секунду, вы очень хорошо можете увидеть снижение производительности (следовательно, обсуждать здесь не стоит - производительность - это ваш вызов).

Я думаю, мне нужно спросить, почему этот подход используется. Действительно ли вы можете добавить значимую информацию об исключениях на уровне на каждом уровне, где может быть сгенерировано исключение, и, если это так, также верно, что информация будет иметь вид:

  • Что-то, что вы на самом деле хотите поделиться со своим пользователем?
  • Что-то, что ваш пользователь сможет интерпретировать, понимать и использовать ?
  • Написано таким образом, чтобы оно не мешало последующему повторному использованию низкоуровневых компонентов, полезность которых может быть неизвестна при их написании?

Я спрашиваю об обмене информацией с вашим пользователем, потому что, в вашем примере, ваш искусственный стек начинается с информирования пользователя о том, что произошла ошибка при аутентификации в базе данных. Для потенциального хакера это хорошая информация, которая раскрывает информацию о том, что делала операция.

Что касается возврата целого стека пользовательских исключений, я не думаю, что это будет полезно большинству (честных) пользователей. Например, если у меня возникают проблемы с получением списка имен клиентов, это поможет мне (как пользователю) узнать, что была проблема с аутентификацией в базе данных? Если вы не используете встроенную аутентификацию, и у каждого из ваших пользователей есть учетная запись и возможность связаться с системным администратором, чтобы выяснить, почему их учетной записи не хватает привилегий, вероятно, нет.

Сначала я бы решил, действительно ли существует семантическая разница между генерируемым исключением Framework и сообщением об исключении, которое вы хотели бы предоставить пользователю. Если это так, то используйте нестандартное исключение на самом низком уровне («ошибка входа в систему» ​​в вашем примере). Следующие шаги, вплоть до фактического представления исключения, на самом деле не требуют каких-либо пользовательских исключений. Интересующее вас исключение уже было сгенерировано (вход в систему не удался) - продолжение переноса этого сообщения на каждом уровне стека вызовов не служит никакой реальной цели, кроме раскрытия вашего стека вызовов вашим пользователям. Для этих «средних» шагов, при условии наличия блоков try / catch, простая стратегия «log and throw» подойдет.

Правда, у этой стратегии есть еще один потенциальный недостаток: она налагает на разработчика ответственность за поддержание внедренного стандарта пользовательских исключений. Поскольку вы не можете знать каждую перестановку иерархии вызовов при написании низкоуровневых типов (их «клиенты», возможно, даже еще не были написаны), маловероятно, что все разработчики - или даже один разработчик - не забудут обернуть и настроить любое состояние ошибки в каждом блоке кода.

Вместо того, чтобы работать снизу вверх, я обычно беспокоюсь об отображении сгенерированных исключений как можно более поздно в процессе (т. Е. Как можно ближе к «вершине» стека вызовов). Обычно я не пытаюсь заменить какие-либо сообщения в исключениях, выдаваемых на низком уровне моих приложений, особенно потому, что использование этих низкоуровневых членов имеет тенденцию становиться все более абстрактными по мере углубления вызова. Я склонен отлавливать и регистрировать исключения на бизнес-уровне и ниже, а затем иметь дело с отображением их понятным образом на уровне представления.

Вот несколько приличных статей о лучших методах обработки исключений:

http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx

http://aspalliance.com/1119

Боже, я получил несколько ... извинений заранее.

3 голосов
/ 21 сентября 2008

Да, исключения являются дорогостоящими, поэтому за вылов и повторное бросание или выбрасывание более полезного исключения необходимо платить.

Но подожди минутку! Если код фреймворка или используемая вами библиотека выдает исключение, значит, все уже отклеилось. Есть ли у вас нефункциональные требования к скорости распространения сообщения об ошибке после исключения? Я сомневаюсь. Это действительно большое дело? Произошло нечто непредвиденное и «исключительное». Главное - предоставить пользователю полезную и полезную информацию.

Я думаю, что вы на правильном пути с тем, что вы делаете.

2 голосов
/ 21 сентября 2008

Там, где я работаю, у нас есть только несколько причин ловить исключения. Мы делаем это только тогда, когда ...

  1. Мы можем что-то с этим поделать - например, мы знаем, что это может иногда случаться, и мы можем исправить это в коде, как это происходит (очень редко).

  2. Мы хотим знать, что это происходит и где (тогда мы просто отбросим исключение).

  3. Мы хотим добавить дружественное сообщение, в этом случае мы оборачиваем исходное исключение в новое исключение, полученное из исключения приложения, и добавляем к нему дружественное сообщение, а затем позволяем ему пузыриться без изменений с этого момента. 1014 *

В вашем примере мы, вероятно, просто отобразили бы «Ошибка входа в систему». anbd оставьте это в покое, регистрируя реальную ошибку и предоставляя причину, по которой пользователь может перейти к исключению, если он тоже этого захочет. (Возможно кнопка в форме ошибки).

  1. Мы хотим полностью исключить исключение и продолжать. Само собой разумеется, что мы делаем это только для ожидаемых типов исключений и только тогда, когда нет другого способа обнаружить условие, которое генерирует исключение.
2 голосов
/ 21 сентября 2008

Конечно, это ужасно неэффективно. Но в тот момент, когда возникает исключение, которое достаточно важно показать конечному пользователю, вас это не должно волновать.

1 голос
/ 21 сентября 2008

Обычно, когда вы имеете дело с исключениями, производительность и эффективность - это наименьшее количество ваших забот. Вам следует больше беспокоиться о том, чтобы что-то сделать, чтобы помочь пользователю справиться с проблемой. Если при записи определенной записи в базу данных возникла проблема, либо откатите изменения назад, либо хотя бы сбросьте информацию о строке, чтобы пользователь не потерял ее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...