Соглашения об исключениях или кодах ошибок

118

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

Какие правила вы используете, чтобы решить, вызывать ли вы исключения или возвращать коды ошибок для отчетов об ошибках?

Хорхе Феррейра
источник

Ответы:

81

В материалах высокого уровня исключения; в низкоуровневых материалах - коды ошибок.

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

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

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

Том Данэм
источник
5
+1 за указание на то, что контекст имеет какое-то отношение к стратегии обработки ошибок.
alx9r
2
@Tom, Хорошие моменты, но исключения вылавливать гарантированно. Как убедиться , что коды ошибок пойманы и не проигнорированы из - за ошибки?
Pacerier
13
Итак, ваш единственный аргумент против исключений заключается в том, что может случиться что-то плохое, если вы забудете перехватить исключение, но вы не рассматриваете столь же вероятную ситуацию, когда вы забудете проверить возвращаемое значение на наличие ошибок? Не говоря уже о том, что вы получаете трассировку стека, когда забываете перехватить исключение, а вы ничего не получаете, когда забываете проверить код ошибки.
Esailija
3
В C ++ 17 появился nodiscardатрибут, который выдает предупреждение компилятора, если возвращаемое значение функции не сохраняется. Немного помогает отловить забытый код ошибки. Пример: godbolt.org/g/6i6E0B
Zitrax
5
Комментарий @ Esailija здесь как раз по теме. Аргумент против исключений здесь берет гипотетический API на основе кода ошибки, где все коды ошибок задокументированы, и гипотетический программист, который читает документацию, правильно определяет все случаи ошибок, которые логически возможны в ее приложении, и пишет код для обработки каждой из них. , затем сравнивает этот сценарий с гипотетическим API на основе исключений и программистом, в котором по какой-то причине один из этих шагов идет не так ... даже несмотря на то, что одинаково легко (возможно, проще ) выполнить все эти шаги правильно в API на основе исключений.
Марк Эмери
62

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

С другой стороны, коды ошибок легче, чем исключения, но их сложнее поддерживать. Проверка ошибок может быть случайно пропущена. Коды ошибок труднее поддерживать, потому что вам нужно вести каталог со всеми кодами ошибок, а затем включать результат, чтобы увидеть, какая ошибка была выдана. Здесь могут помочь диапазоны ошибок, потому что, если единственное, что нас интересует, это наличие ошибки или ее отсутствие, это проще проверить (например, код ошибки HRESULT больше или равен 0 - успех, а меньше нуля - отказ). Их можно непреднамеренно пропустить, потому что нет программного принуждения, которое разработчик будет проверять на наличие кодов ошибок. С другой стороны, нельзя игнорировать исключения.

Подводя итог, я предпочитаю исключения кодам ошибок почти во всех ситуациях.

Хорхе Феррейра
источник
4
«Коды ошибок легче, чем исключения», зависит от того, что вы измеряете и как вы измеряете. Довольно легко придумать тесты, которые показывают, что API на основе исключений могут быть намного быстрее.
Mooing Duck
1
@smink, Хорошие моменты, но как решить проблему накладных расходов на исключения? Коды ошибок не просто легковесны, они практически невесомы ; исключения не просто средние по весу, они представляют собой тяжелые объекты, содержащие информацию о стеке и разный материал, который мы в любом случае не используем.
Pacerier
3
@Pacerier: вы рассматриваете только тесты, в которых выбрасываются исключения. Вы на 100% правы в том, что генерирование исключения C ++ значительно медленнее, чем возврат кода ошибки. Без споров. Где мы действительно отличаются, это другая 99,999% кода. За исключением случаев, нам не нужно проверять возврат кода ошибки между каждым оператором, что делает этот код на 1-50% быстрее (или нет, в зависимости от вашего компилятора). Это означает, что полный код может работать быстрее или медленнее, полностью в зависимости от того, как написан код и как часто возникают исключения.
Mooing Duck
1
@Pacerier: С этим искусственным тестом, который я только что написал, код на основе исключений работает так же быстро, как коды ошибок в MSVC и Clang, но не в GCC: coliru.stacked-crooked.com/a/e81694e5c508945b (время внизу). Похоже, что используемые мной флаги GCC генерировали необычно медленные исключения по сравнению с другими компиляторами. Я предвзят, поэтому, пожалуйста, критикуйте мой тест и не стесняйтесь пробовать другой вариант.
Mooing Duck
4
@Mooing Duck, если вы тестировали и коды ошибок, и исключения одновременно, значит, у вас должны быть включены исключения. Если это так, ваши результаты предполагают, что использование кодов ошибок с накладными расходами на обработку исключений не медленнее, чем использование только исключений. Чтобы получить значимые результаты, тесты кода ошибки необходимо выполнять с отключенными исключениями.
Мика Хаарахилтунен
24

Я предпочитаю исключения, потому что

  • они прерывают поток логики
  • они извлекают выгоду из иерархии классов, которая дает больше возможностей / функций
  • при правильном использовании может представлять широкий спектр ошибок (например, InvalidMethodCallException также является LogicException, поскольку оба возникают, когда в вашем коде есть ошибка, которую следует обнаружить до выполнения), и
  • их можно использовать для улучшения ошибки (то есть определение класса FileReadException может затем содержать код для проверки того, существует ли файл, заблокирован и т. д.)
JamShady
источник
2
Ваш четвертый пункт не совсем справедлив: статус ошибки при преобразовании в объект также может содержать код для проверки того, существует ли файл, заблокирован и т. Д. Это просто вариант stackoverflow.com/a/3157182/632951
Pacerier
1
«они прерывают поток логики». Исключения имеют более или менее эффектgoto
peterchaula
22

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

Maxam
источник
17

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

Вот несколько статей, в которых обсуждаются, сравниваются и противопоставляются эти две техники:

В них есть несколько хороших ссылок, по которым можно почитать дальше.

Pistos
источник
16

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

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

Исключения позволяют писать (и использовать) методы с семантикой, зависящей от функции метода, то есть метод, который возвращает объект Employee или List of Employees, может быть напечатан для этого, и вы можете использовать его, вызвав.

Employee EmpOfMonth = GetEmployeeOfTheMonth();

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

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

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

Чарльз Бретана
источник
Отличный ответ! Готовое решение как раз вовремя!
Cristian E.
11

В прошлом я присоединился к лагерю кодов ошибок (слишком много программировал на C). Но теперь я увидел свет.

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

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

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

Мультяшный Крайте
источник
1
yap C действительно оставляет у всех нас несколько привычек;)
Хорхе Феррейра
11

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

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

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

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

... предпочтительнее этого:

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

Есть и другие приятные мелочи об исключениях. Наличие связки условной логики для отслеживания того, был ли какой-либо из методов, вызываемых в функции, возвращенным кодом ошибки, и возвращение этого кода ошибки выше - это много шаблонов. На самом деле, может выйти из строя множество котлов. Я гораздо больше верю в систему исключений большинства языков, чем в крысиное гнездо утверждений if-else-if-else, написанных Фредом из "Новенького колледжа", и у меня есть много дел поинтереснее. с моим временем, чем просмотр кода, сказал крысиное гнездо.

Собаки
источник
8

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

Есть несколько сценариев, в которых исключения являются очевидным выбором :

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

  2. Тем не менее, что касается ситуации 1 (когда происходит что-то неожиданное и неуправляемое, вы просто не хотите регистрировать это), исключения могут быть полезны, потому что вы можете добавить контекстную информацию . Например, если я получаю SqlException в моих помощниках по данным нижнего уровня, я захочу перехватить эту ошибку на низком уровне (где я знаю команду SQL, вызвавшую ошибку), чтобы я мог захватить эту информацию и повторно выбросить с дополнительной информацией , Обратите внимание на волшебное слово здесь: перебросить, а не проглотить . Первое правило обработки исключений: не проглатывайте исключения . Также обратите внимание, что моему внутреннему улову не нужно ничего регистрировать, потому что внешний улов будет иметь всю трассировку стека и может регистрировать его.

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

    Одним из примеров может быть последовательность операторов SQL внутри блока транзакции. Опять же, это также «неуправляемая» ситуация, даже если вы решите ее вовремя поймать (лечить ее локально, а не подниматься наверх), это все еще фатальная ситуация, из которой лучшим выходом будет все прервать или хотя бы прервать большой часть процесса.
    (*) Это похоже на on error gotoто, что мы использовали в старом Visual Basic

  4. В конструкторах можно создавать только исключения.

Сказав это, во всех других ситуациях, когда вы возвращаете некоторую информацию, по которой вызывающий МОЖЕТ / ДОЛЖЕН предпринять какие-то действия , использование кодов возврата, вероятно, является лучшей альтернативой. Это включает в себя все ожидаемые «ошибки» , потому что, вероятно, они должны быть обработаны непосредственным вызывающим абонентом, и вряд ли их нужно будет поднимать на слишком много уровней в стеке.

Конечно, всегда можно обрабатывать ожидаемые ошибки как исключения и сразу же перехватывать их на один уровень выше, а также можно охватить каждую строку кода в try catch и предпринять действия для каждой возможной ошибки. IMO, это плохой дизайн не только потому, что он намного более подробный, но особенно потому, что возможные исключения, которые могут быть выброшены, не очевидны без чтения исходного кода - и исключения могут быть выброшены из любого глубокого метода, создавая невидимые переходы . Они нарушают структуру кода, создавая несколько невидимых точек выхода, которые затрудняют чтение и проверку кода. Другими словами, вы никогда не должны использовать исключения для управления потоком., потому что другим будет трудно понять и поддерживать. Может быть даже сложно понять все возможные потоки кода для тестирования.
Опять же: для правильной очистки / удаления вы можете использовать try-finally, ничего не ловя .

Самая популярная критика кодов возврата заключается в том, что «кто-то может игнорировать коды ошибок, но в том же смысле кто-то может также принимать исключения. Плохая обработка исключений проста в обоих методах. Но написать хорошую программу, основанную на кодах ошибок, все же намного проще. чем писать программу, основанную на исключениях.И если кто-то по какой-либо причине решает игнорировать все ошибки (старые on error resume next), вы можете легко сделать это с помощью кодов возврата, и вы не можете сделать это без большого количества шаблонов try-catch.

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

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

Подвести итог:

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

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

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

Тем не менее, что касается GO, он также позволяет несколько возвращаемых значений , что очень помогает при использовании кодов возврата, поскольку вы можете одновременно возвращать ошибку и что-то еще. В C # / Java мы можем добиться этого с помощью параметров, кортежей или (моих любимых) универсальных шаблонов, которые в сочетании с перечислениями могут предоставить вызывающей стороне четкие коды ошибок:

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

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

drizin
источник
4

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

Во всех остальных случаях, вероятно, лучше использовать исключения.

Клаудиу
источник
4

Я могу сидеть здесь на заборе, но ...

  1. Это зависит от языка.
  2. Какую бы модель вы ни выбрали, будьте последовательны в том, как вы ее используете.

В Python использование исключений является стандартной практикой, и я вполне счастлив определить свои собственные исключения. В C у вас вообще нет исключений.

В C ++ (по крайней мере, в STL) исключения обычно создаются только для действительно исключительных ошибок (я практически никогда их не вижу). Я не вижу причин делать что-то другое в собственном коде. Да, легко игнорировать возвращаемые значения, но C ++ также не заставляет вас ловить исключения. Я думаю, тебе просто нужно выработать привычку это делать.

Кодовая база, над которой я работаю, - это в основном C ++, и мы используем коды ошибок почти везде, но есть один модуль, который вызывает исключения для любых ошибок, включая очень обычные, и весь код, который использует этот модуль, довольно ужасен. Но это может быть только потому, что мы смешали исключения и коды ошибок. С кодом, который постоянно использует коды ошибок, намного проще работать. Если бы наш код постоянно использовал исключения, возможно, это было бы не так плохо. Смешивание этих двух вещей, похоже, не так хорошо работает.

JRB
источник
4

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

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

Затмение
источник
3

Сигнатуры методов должны сообщать вам, что делает метод. Что-то вроде long errorCode = getErrorCode (); может быть хорошо, но длинный errorCode = fetchRecord (); сбивает с толку.

Пол Кроаркин
источник
3

Мой подход заключается в том, что мы можем использовать оба кода, т.е. коды исключений и ошибок одновременно.

Я привык определять несколько типов исключений (например, DataValidationException или ProcessInterruptExcepion), и внутри каждого исключения определять более подробное описание каждой проблемы.

Простой пример на Java:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

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

Сакана
источник
2
эээ ... throw new DataValidationException("The input is too small")? Одно из преимуществ исключений - предоставление подробной информации.
Ева
2

Исключения предназначены для исключительных обстоятельств, т. Е. Когда они не являются частью нормального потока кода.

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

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

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

Но если пойти в другом направлении, то использование исключений позволяет создавать абстракции еще более высокого уровня для обработки ошибок, что может сделать ваш код еще более выразительным и естественным. Я настоятельно рекомендую прочитать эту превосходную, но недооцененную статью эксперта по C ++ Андрея Александреску на тему того, что он называет «Enforcements»: http://www.ddj.com/cpp/184403864 . Хотя это статья на C ++, принципы в целом применимы, и я довольно успешно перевел концепцию принудительного исполнения на C #.

philsquared
источник
2

Во-первых, я согласен с ответом Тома, что для высокоуровневых материалов используйте исключения, а для низкоуровневых - используйте коды ошибок, если это не сервис-ориентированная архитектура (SOA).

В SOA, где методы могут вызываться на разных машинах, исключения могут не передаваться по сети, вместо этого мы используем ответы об успехе / неудаче со структурой, подобной приведенной ниже (C #):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

И используйте вот так:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

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

Orad
источник
1

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

Возврат недействительных указателей, которые могут быть разыменованы (например, вызывая исключение NullPointerException в Java), неприемлем.

Использование нескольких различных числовых кодов ошибок (-1, -2) в качестве возвращаемых значений для одной и той же функции обычно является плохим стилем, поскольку клиенты могут выполнять проверку «== -1» вместо «<0».

Здесь следует помнить об эволюции API с течением времени. Хороший API позволяет изменять и расширять поведение при сбоях несколькими способами, не нарушая работу клиентов. Например, если дескриптор ошибки клиента проверен на наличие 4 случаев ошибок, и вы добавляете пятое значение ошибки в свою функцию, обработчик клиента может не проверить это и сломаться. Если вы вызываете исключения, это обычно упрощает переход клиентов на более новую версию библиотеки.

Еще одна вещь, которую следует учитывать при работе в команде, - где провести четкую границу, чтобы все разработчики приняли такое решение. Например, «Исключения для высокоуровневых вещей, коды ошибок для низкоуровневых вещей» очень субъективно.

В любом случае, когда возможно более одного тривиального типа ошибки, исходный код никогда не должен использовать числовой литерал для возврата кода ошибки или для его обработки (возврат -7, если x == -7 ...), но всегда именованная константа (возвращает NO_SUCH_FOO, если x == NO_SUCH_FOO).

tkruse
источник
1

Если вы работаете над большим проектом, вы не можете использовать только исключения или только коды ошибок. В разных случаях следует использовать разные подходы.

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

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

gomons
источник
0

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

Джим С
источник
0

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

например, когда вы обрабатываете файл и сталкиваетесь с IOException, клиент может быть заинтересован в том, чтобы узнать, откуда это было инициировано, в открытии файла или анализе файла и т. д. Так что лучше вернуть IOException или его конкретный подкласс. Однако в случае, когда у вас есть метод входа в систему, и вы хотите знать, что он был успешным или нет, вы либо просто возвращаете логическое значение, либо показываете правильное сообщение, возвращаете код ошибки. Здесь Клиента не интересует, какая часть логики вызвала этот код ошибки. Он просто знает, недействительны ли его учетные данные или заблокирована ли учетная запись и т. Д.

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

ashah
источник
0

Мое общее правило:

  • В функции могла появиться только одна ошибка: используйте код ошибки (как параметр функции)
  • Могло появиться более одной конкретной ошибки: исключение исключения
DeaDBeeF
источник
-1

Коды ошибок также не работают, когда ваш метод возвращает что-либо, кроме числового значения ...

Омар Кухеджи
источник
3
Гм, нет. См. Парадигму Win32 GetLastError (). Я не защищаю это, просто утверждаю, что вы не правы.
Тим,
4
На самом деле есть много других способов сделать это. Другой способ - вернуть объект, содержащий код ошибки и реальное возвращаемое значение. Еще один способ - передать ссылку.
Pacerier