Обработка (возможно, меняющихся) кодов ошибок библиотеки с использованием исключений

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

Если библиотека все еще находится в разработке кем-то другим, и если коды ошибок могут меняться (могут быть новые, могут быть устаревшие, или некоторые коды ошибок могут немного изменить значение), каково было бы ваше решение справиться с этим?

Это та ситуация, в которой я сейчас. В моем случае библиотека написана на С++, и мы используем С#. Кодер библиотеки говорит, что коды ошибок могут меняться, и я должен найти способ работать с ним.

Наше начальное решение:

  • Создайте XML файл с кодами ошибок в разных категориях (терминальные, входные и другие).
  • Wrapper извлекает эти коды ошибок при запуске.
  • Выдает соответствующее исключение, проверяя категорию кода ошибки.

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

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

Что вы предлагаете делать?

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

Ответ 1

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

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

Ответ 2

Я думаю, вы решите эту проблему легче, если немного измените свое видение ситуации:

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

Теперь вопрос сводится к следующему: внутренняя инфраструктурная функциональность четко обозначена и финализирована? или это тоже меняется?

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

Теперь обработка ошибок:

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

Что это значит для вас:

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

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

Пример:

//external.
int Say(char* message);

//internal.
///<summary>
/// can throw (CONTRACT): WrongMessageException, SessionTimeOutException
void Say(string message) {
    int errorCode = External.Say(message);
    //translate error code to either WrongMessageException or to SessionTimeOutException.
}

Невозможно перевести? что-то не так, как с текущими контрактными ошибками, так и с внешней картой: возможно, вам следует прекратить процесс? что-то пошло не так, неожиданно!!!

//client.
...
try {
    Internal.Say("Hello");
}
catch (WrongMessageException wme) {
    //deal with wrong message situation.
}
catch (SessionTimeOutException stoe) {
    //deal with session timeout situation.
}

Сообщите мне, если что-нибудь поднимает вопрос.

Перевод кодов ошибок в Исключения:

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

Ниже приведена псевдо-конфигурация. Возьмите это как первоначальное представление о том, как классифицировать:

category Say [can throw]: { WrongMessageException, SessionTimeOutException }
category WrongMessageException [by error code]: { 100, 101 }
category SessionTimeOutException [by error code]: { 102, 103, 104 }

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

Ссылка

Книга: Джеффри Рихтер - CLR через С#, 3-е издание. Глава 20 - Исключения и управление государством. Подглад - Руководящие принципы и передовая практика. Sub-Sub-Chapter - Скрытие детали реализации для поддержки "Контракта".

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

Ответ 3

Как насчет этого:

Вы сказали, что сохранили категории ошибок где-то (DB или XML файл) позволяет развлечься, у нас есть некоторые таблицы основных деталей, называемые ErrorCategory (Master) и ErrorDetail (Detail)
Я рекомендую добавить столбец (свойство) в таблицу ошибок Вызывается CustomExceptionType, это будет текстовое свойство, содержащее полное имя сборки и имя класса указанного исключения (например: CustomExceptions, CustomExceptions.TerminalError)
Нам понадобится базовый класс 4 для всех наших пользовательских исключений, назовем его BaseCustomException calss
Нам нужен класс ExceptionFactory, позвонив ему CustomExceptionFactory class
У нашего ExceptionFactory будет метод CreateException, что-то вроде этого

Public BaseCustomException CreateException(EceptinCategory category, ExceptionDetail detail)
{
   var customException = Activator.CreateInstance(category.CustomExceptionType) as      BaseCustomException;
   customException.SetDetails(detail);
   return customException;
}

поэтому во время выполнения наш объект CustomExceptionFactory будет использовать CustomExceptionType для создания экземпляра конкретного исключения с использованием Reflection.
Я предпочитаю CustomExceptionFactory и BaseCustomException для реализации на собрании и все производные CustomExceptions будут реализованы в другой сборке, поэтому наше основное приложение будет не связано с CustomExceptions.Dll
В futur путем смены С++ factory нашему главному приложению не потребуется перестройка, и все, что нам нужно, это изменение данных таблиц и impementaion в CustomExceptions.Dll. (одно и то же решение может быть реализовано с использованием XML или файла конфигурации или...)

Это поможет.

Ответ 4

ОК, если вы хотите быть гибким и не зависеть от кода, я думаю, что использование отражения для создания пользовательского класса при первом запуске приложения было бы лучшим. Вот грубое объяснение. Если вам это нравится, я могу объяснить это дальше. Поставщик кода на С++ должен создать класс, который будет содержать все коды ошибок - например, public class Errors {public static readonly IOError = 100}. При запуске приложения вы проверите этот класс для модификации, и если он будет изменен, вы создадите класс исключения для каждого кода ошибки. В приведенном выше примере вы создадите класс IoException, наследующий класс Exception.net. После этого вы можете использовать его в обертке и каждый отдельный вывод. Другим возможным решением является изменение xml, о котором вы упомянули, - для каждого кода ошибки добавьте класс исключения - используя пример для кода ошибки 100, вы будете иметь класс IoException. после этого вам нужно реализовать этот класс и использовать его...

Ответ 5

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

Кроме того, кажется, что достаточно просто создать CSV или XML с парами кодовых строк, которые разработчик алгоритма может свободно редактировать по своему усмотрению. Зарезервируйте определенные диапазоны кодового номера для различных типов ошибок (1000s для ошибок ввода, 2000 секунд для ошибок терминала и т.д.), Чтобы ваша оболочка интерпретировала код возврата с помощью пары кодовых строк, которую он написал.

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