Обработка ошибок без исключений

В одном из моих заданий в начале 90-х, мы использовали .cc и .hh для исходных и заголовочных файлов соответственно. Я все еще предпочитаю его по всем альтернативам, вероятно, потому что является самым легким ввести.

21
задан James 10 June 2011 в 22:22
поделиться

6 ответов

В приведенном вами примере пользовательский интерфейс проверяет вводимые данные.

Таким образом, хороший подход - отделить валидацию от действия. WinForms имеет встроенную систему проверки, но в принципе она работает следующим образом:

ValidationResult v = ValidateName(string newName);
if (v == ValidationResult.NameOk)
    SetName(newName);
else
    ReportErrorAndAskUserToRetry(...);

Кроме того, вы можете применить проверку в методе SetName, чтобы убедиться, что валидность проверена:

public void SetName(string newName)
{
    if (ValidateName(newName) != ValidationResult.NameOk)
        throw new InvalidOperationException("name has not been correctly validated");

    name = newName;
}

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

Процитируем другой ответ:

Either a member fulfills its contract or it throws an exception. Period.

Здесь упускается следующее: что такое контракт? Совершенно разумно указать в «контракте», что метод возвращает значение статуса. например File.Exists () возвращает код состояния, а не исключение, потому что это его контракт.

Однако ваш пример другой. В нем вы фактически выполняете два отдельных действия: проверку и сохранение. Если SetName может либо вернуть код состояния , либо установить имя, он пытается выполнить две задачи в одной, что означает, что вызывающий никогда не знает, какое поведение он будет демонстрировать, и должен иметь особую обработку случаев для те случаи. Однако, если вы разделите SetName на отдельные шаги Validate и Store, тогда контракт для StoreName может заключаться в том, что вы передаете действительные входные данные (как передано ValidateName), и он генерирует исключение, если этот контракт не выполняется. Поскольку каждый метод затем выполняет одно и только одно действие, контракт очень ясен, и очевидно, когда должно быть создано исключение.

3
ответ дан 29 November 2019 в 22:06
поделиться

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

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

  1. Исключения сериализуемы
  2. Исключения всегда имеют свойство Сообщение , удобочитаемое человеком, и могут иметь дополнительные свойства для записи деталей исключения в машиночитаемой форме.
  3. Некоторые из сбоев бизнес-правил могли на самом деле быть сигнализированы исключениями - например, FormatException . Вы можете перехватить это исключение и добавить его в список.

Фактически,

4
ответ дан 29 November 2019 в 22:06
поделиться

Думаю, у вас неправильное впечатление о предполагаемом сообщении. Вот отличная цитата, которую я вчера встретил из текущего выпуска журнала Visual Studio (Том 19, № 8).

Либо участник выполняет свой контракт, либо выдает исключение. Период. Нет золотой середины. Никаких кодов возврата, нет, иногда это работает, иногда нет.

Исключения следует использовать с осторожностью, поскольку их создание и выбрасывание дорого, но они, однако, являются способом .NET framework для уведомления клиента ( под этим я подразумеваю любой вызывающий компонент) ошибки.

4
ответ дан 29 November 2019 в 22:06
поделиться

Я согласен с частью предложения Хенка.

Традиционно операции «прошел / не прошел» были реализованы как функции с целочисленным или логическим типом возвращаемого значения, которые указывали бы результат вызова. Однако некоторые возражают против этого, заявляя, что «функция или метод должны либо выполнять действие, либо возвращать значение, но не то и другое вместе». Другими словами, член класса, который возвращает значение, не должен также быть членом класса, который изменяет состояние объекта.

Я нашел лучшее решение, добавив .HasErrors / .IsValid и . Errors свойство внутри класса, генерирующего ошибки. Первые два свойства позволяют клиентскому классу проверять, существуют ли ошибки, и, если необходимо, также может читать свойство .Errors и сообщать об одной или всех содержащихся ошибках. Затем каждый метод должен знать об этих свойствах и соответствующим образом управлять состоянием ошибки. Эти свойства затем можно преобразовать в интерфейс IErrorReporting, который могут включать различные классы фасадов уровня бизнес-правил.

2
ответ дан 29 November 2019 в 22:06
поделиться

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

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

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

  • Произошло что-то катастрофическое и системное, например, нехватка памяти.
  • Программист пошел и запрограммировал что-то неправильно, будь то неправильный вызов метода или фрагмент недопустимого SQL.

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

  • Произошло что-то катастрофическое и системное, например, нехватка памяти.
  • Программист пошел и запрограммировал что-то неправильно, будь то неправильный вызов метода или фрагмент недопустимого SQL.

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

  • Произошло что-то катастрофическое и системное, например, нехватка памяти.
  • Программист пошел и запрограммировал что-то неправильно, будь то неправильный вызов метода или фрагмент недопустимого SQL.

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

  • Программист пошел и запрограммировал что-то неправильно, будь то неправильный вызов метода или фрагмент недопустимого SQL.
  • Они не должны быть единственной линией защиты от ошибок пользователя. Пользователям требуется гораздо больше отзывов и внимания, чем может предложить какое-либо исключение, и они будут регулярно

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

    0
    ответ дан 29 November 2019 в 22:06
    поделиться

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

    Например, если у вас есть классы бизнес-домена, которые используются приложением WPF и службой WCF, недопустимый ввод поля может быть обычным делом в приложении WPF, но это будет иметь катастрофические последствия, если объекты домена используются в Ситуация WCF, когда вы обрабатываете запросы на обслуживание от другого приложения.

    Я долго и упорно думал, и пришел к этому решению. Выполните следующие действия с классами домена:

    • Добавьте свойство: ThrowsOnBusinessRule. По умолчанию должно быть true, чтобы генерировать исключения. Установите значение false, если вы не хотите его бросать.
    • Добавьте частную коллекцию словарей для хранения исключений с ключом в качестве свойства домена, которое нарушает бизнес-правила. (Конечно, вы можете сделать это публично, если хотите)
    • Добавьте метод: ThrowsBusinessRule (string propertyName, Exception e) для обработки вышеуказанной логики
    • Если хотите, вы можете реализовать IDataErrorInfo и использовать коллекцию Dictionary. Реализация IDataErrorInfo тривиальна, учитывая приведенную выше настройку.
    1
    ответ дан 29 November 2019 в 22:06
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: