Чтение статьи об исключениях Эрика Липперта определенно открыло мне глаза на то, как мне следует подходить к исключениям как в качестве производителя, так и потребителя. Тем не менее, я все еще изо всех сил пытаюсь определить руководство относительно того, как избежать создания неприятных исключений.
В частности:
- Предположим, у вас есть метод Save, который может потерпеть неудачу, потому что a) Кто-то еще изменил запись перед вами , или b) Значение, которое вы пытаетесь создать, уже существует . Эти условия должны быть ожидаемыми и не исключительными, поэтому вместо создания исключения вы решаете создать Try-версию вашего метода TrySave, который возвращает логическое значение, указывающее, успешно ли выполнено сохранение. Но если это не поможет, как потребитель узнает, в чем проблема? Или было бы лучше вернуть перечисление с указанием результата, вроде Ok / RecordAlreadyModified / ValueAlreadyExists? С integer.TryParse эта проблема не существует, так как есть только одна причина, по которой метод может завершиться ошибкой.
- Действительно ли предыдущий пример является неприятной ситуацией? Или выбрасывание исключения в этом случае будет предпочтительным способом? Я знаю, как это делается в большинстве библиотек и сред, включая платформу Entity.
- Как вы решаете, когда создавать версию Try вашего метода, а не предоставлять какой-либо способ заранее проверить, будет ли метод работать или нет? В настоящее время я следую этим рекомендациям:
- Если есть вероятность возникновения гонки, создайте пробную версию. Это предотвращает необходимость для потребителя поймать экзогенное исключение. Например, в методе Save, описанном ранее.
- Если метод для проверки условия в значительной степени будет делать все то же, что и оригинальный метод, то создайте версию Try. Например, integer.TryParse ().
- В любом другом случае создайте метод для проверки условия.
exceptions
Майк
источник
источник
Ответы:
Хороший вопрос.
Первый вопрос, который мне приходит в голову: если данные уже есть, то в каком смысле сбой сохранения был неудачным ? Похоже, мне это удалось. Но давайте предположим ради аргумента, что у вас действительно есть много разных причин, почему операция может завершиться неудачей.
Второй вопрос, который приходит мне в голову: является ли информация, которую вы хотите вернуть пользователю, действующей ? То есть они собираются принять какое-то решение на основе этой информации?
Когда загорается лампочка проверки двигателя, я открываю капот, проверяю, есть ли в моей машине двигатель, который не горит, и доставляю его в гараж. Конечно, в гараже у них есть все виды специального диагностического оборудования, которое говорит им, почему горит контрольная лампа двигателя, но, с моей точки зрения, система предупреждения хорошо спроектирована. Мне все равно, проблема в том, что датчик кислорода регистрирует ненормальный уровень кислорода в камере сгорания, или потому что датчик холостого хода отключен, или что-то еще. Я собираюсь предпринять то же самое, а именно, пусть кто-то еще выяснит это .
Звонит ли вызывающему абоненту, почему сохранение не удалось? Собираются ли они что-нибудь с этим сделать, кроме как сдаться или попробовать снова?
Предположим ради аргумента, что вызывающая сторона действительно предпримет разные действия в зависимости от причины сбоя операции.
Третий вопрос, который приходит на ум: является ли режим отказа исключительным ? Я думаю, что вы можете путать возможное с необычным . Я думаю, что два пользователя пытаются изменить одну и ту же запись одновременно как исключительную, но возможную ситуацию, а не обычную ситуацию.
Давайте предположим ради аргумента, что это не является исключением.
Четвертый вопрос, который приходит на ум: есть ли способ надежного выявления плохой ситуации заранее?
Если плохая ситуация в моем «экзогенном» ведре, то нет. Там нет никакого способа надежно сказать "другой пользователь изменил эту запись?" потому что они могут изменить его после того, как вы зададите вопрос . Ответ устарел, как только он произведен.
Пятый вопрос, который приходит на ум: есть ли способ спроектировать API, чтобы можно было предотвратить плохую ситуацию?
Например, вы могли бы сделать операцию «сохранения» требующей двух шагов. Шаг первый: получить блокировку изменяемой записи. Эта операция либо завершается успешно, либо завершается неудачей и может возвращать логическое значение. В этом случае вызывающий абонент может иметь политику действий при сбое: подождать некоторое время и повторить попытку, сдаться, что угодно. Шаг второй: как только блокировка получена, сохраните и отпустите блокировку. Теперь сохранение всегда происходит успешно, поэтому нет необходимости беспокоиться о какой-либо обработке ошибок. Если сохранение не удается, это действительно исключительное.
источник
В вашем примере, если ситуация ValueAlreadyExists может быть легко проверена, она должна быть проверена, и перед попыткой сохранения может возникнуть исключение, я не думаю, что попытка должна быть необходимой в этой ситуации. Сложнее заранее проверить состояние гонки, поэтому в этом случае, вероятно, стоит добавить «Сохранить в попытке».
В общем, если есть условие, которое я считаю весьма вероятным (например, NoDataReturned, DivideByZero и т. Д.) ИЛИ очень легко проверить (например, пустая коллекция или значение NULL), я пытаюсь проверить наличие опередить время и разобраться с этим, прежде чем я доберусь до точки, где мне придется поймать исключение. Я признаю, что не всегда легко узнать эти условия заранее, иногда они появляются только тогда, когда код подвергается тщательному тестированию.
источник
save()
Метод должен бросить исключение.Самый верхний слой должен перехватывать и информировать пользователя , не завершая программу, если только это не Unix-подобные программы командной строки, и в этом случае можно прекратить работу.
Возвращаемые значения не являются хорошим способом управления исключениями.
источник