Как вы называете и упорядочиваете свои исключения?

#delphi #exception #coding-style

#delphi #исключение #стиль кодирования

Вопрос:

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

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

Это также влияет на именование. Какой объем контекста вы включаете? Что лучше — сделать их очень специфичными (например, EPersonIDNotFoundError) или попытаться сделать их повторно используемыми (например, ENotFoundError)?

А как насчет суффикса «Ошибка» — когда я должен его добавить, а когда оставить? Я не вижу логики, например, в Classes.pas :

   EWriteError = class(EFilerError);
  EClassNotFound = class(EFilerError);
  EResNotFound = class(Exception);
  

Комментарии:

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

2. Это не вопрос… Слишком много вопросов сразу, с потенциальной дискуссией (троллинг?). ИМХО, это не очень подходит для цели SO.

3. @Arnaud Что ж, позвольте мне попытаться ответить на этот вопрос. Потому что я действительно ценю ваш вклад.

Ответ №1:

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

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

Ошибка эконовертера, Ошибка эматерра, ошибка Эварианта

против

Нарушение доступа, EInvalidPointer, EZeroDivide

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

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

Комментарии:

1. Хороший момент в пропуске «Ошибки», когда само сообщение об ошибке довольно хорошо передает состояние сбоя. Вы смешиваете «Исключение» и «Ошибку»?

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

Ответ №2:

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

Итак, в вашем примере я бы использовал EPersonIDNotFoundError, ENotFoundError, Exception.

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

Ответ №3:

Обычно для простых приложений вы можете обойтись исключением.Создать(‘ErrorMessage’). Где исключения становятся мощными, так это способность определять тип требуемого ответа, просматривая класс. Delphi уже делает это с помощью процедуры прерывания, которая вызывает EAbort. EAbort «особенный» в том смысле, что он не вызывает «ошибку» как таковую, т. е. это своего рода «молчащее» исключение. Вы можете использовать это специфичное для класса действие для проверки исключения и выполнения разных действий. Вы могли бы создать EMyWarning, EMyVeryNastyError и т.д., Каждый из которых произошел от базового типа исключения.

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

 EMyException = class( Exception )
  constructor Create( const AErrorMessage : string; AErrorCode : integer ); reintroduce;
PUBLIC
  ErrorCode : integer
end;

constructor EMyException.Create( const AErrorMessage : string; AErrorCode : integer );
begin
  inherited Create( AErrorMessage );
  ErrorCode := AErrorCode;
end;
  

Теперь у вас есть возможность задать ‘ErrorCode’ при возникновении исключения, и он будет доступен при перехвате исключения. Исключения довольно мощные.

Комментарии:

1. На самом деле это не ответ на вопрос, но хорошая демонстрация того, зачем вообще наследовать от Exception. И мне нравится EAbort. 🙂 Вы можете отменить так много вещей. Например, прервать публикацию записи, вызвав EAbort в OnBeforePost и многие подобные прерывания. Я также часто использую его для прерывания сложных операций.

Ответ №4:

В какой области их упорядочивать?

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

Как насчет именования?

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

Как насчет постфикса «Ошибка»?

Перестаньте думать об этом. Добавьте это. Если это не звучит глупо.

Комментарии:

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

2. @mjn Абсолютно. В противном случае пользователи интерфейса не смогут выполнять специальную проверку по классам исключений, не получая зависимости от разработчиков…