Обработка исключений Delphi, используйте E: Exception или ExceptObject

У нас есть 2 приложения, первый - проект VCL, другой - служба Windows.

В проекте VCL мы делаем:

try
except
  on E: Exception do
    // do something with E.Message
end

но в службе windows (которая использует несколько потоков) мы используем:

try
except
    // do something with Exception(ExceptObject).Message
end

Информация, полученная от моих сотрудников, заключается в том, что "мы должны использовать ExceptObject в потоках и E: Исключение в приложениях, которые используют GUI". Но я ничего не мог найти по этому поводу.

Я нашел здесь пример http://edn.embarcadero.com/article/10452, где он использует переменную экземпляра для хранения исключения и использует ExceptObject, но не дает объяснений почему.

Является ли этот ExceptObject даже потокобезопасным (поскольку он исходит от устройства "Система" )?

Итак, каков правильный способ обработки исключений в Delphi и почему существует более чем один способ сделать это?

Ответ 1

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

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

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

1. Получить текущий объект исключения из ExceptObject

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

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

Таким образом, фактически работа с объектом исключения, полученным из выражения on, может быть более стабильной, чем использование ExceptObject. Но здесь действуют те же правила; вам нужно будет синхронизировать экземпляр объекта с выражением on с другим потоком (так как он не является потокобезопасным классом), но в этом случае объект, полученный из на выражение не будет изменено в отличие от ExceptObject, которое может находиться в определенном блоке исключений.

2. Сохранять объект исключения с помощью метода AcquireExceptionObject

Функция AcquireExceptionObject позволяет сохранить объект исключения даже вне блока исключений.

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

Ответ 2

Виктория абсолютно правильна.

Лично я предпочитаю эту идиому:

try
  ...
   except
     // IO error
     On E : EInOutError do
       ShowMessage('IO error : '+E.Message);
     // Dibision by zero
     On E : EDivByZero do
       ShowMessage('Div by zero error : '+E.Message);
     // Catch other errors
     else
       ShowMessage('Unknown error');
   end;

Разработать:

  • Виктория сказала: "Не существует правильного способа обработки исключений. Существует только один способ". Это абсолютно правильно.

  • Совет, который вы получили о "использовании одного синтаксиса для потоков, а другой для графических интерфейсов" - это просто неправильно. Для "потоков" и "графического интерфейса" нет "различного синтаксиса". Эта чепуха: (

  • Я предпочитаю использовать on : MyExceptionType в блоке exception.

  • Я также предпочитаю различать разные типы исключений, когда это возможно.

  • Пример, который вы указали, http://edn.embarcadero.com/article/10452, касается того, как избежать возможного нарушения прав доступа, если вы не обрабатываете исключение в этом конкретном потоке. Сохранение экземпляра исключения в переменной-члене помогает смягчить эту проблему.

Следующая ссылка может помочь уточнить: