Использование блоков FINALLY
Конечные блоки FINALLY обеспечивают обработку окончания блока для базовых блоков ABL. Эта обработка выполняется на каждой итерации блока, даже если эта итерация привела к ошибке.
Введение
В объектно-ориентированном программировании, «очищающий» код, удаляющий ненужные объекты и освобождающий ресурсы, является жизненно важным. Оператор FINALLY поддерживает выполнение этой задачи. Оператор FINALLY создает конечный блок, который исполняется каждый раз в конце итерации связанного блока, независимо от того, закончилась итерация успешно или установила состояние ERROR.
Блок FINALLY исполняется после:
- Успешного выполнения связанного блока
- Каждой успешной итерации связанного блока
- Установки ERROR в связанном блоке и обработки его в блоке CATCH
- Установки ERROR в связанном блоке и нет блока CATCH для его обработки
Блок FINALLY не выполняется, если:
- Установлено и не обработано состояние STOP
- Установлено и не обработано состояние QUIT
В каждом связанном блоке может быть только один блок FINALLY. Он должен располагаться после всех выполняемых операторов связанного блока. Если в связанном блоке имеются операторы CATCH, блок FINALLY должен располагаться после них. Отметим, что блок FINALLY может быть использован в блоке, не имеющем блоков CATCH.
Назначение блока FINALLY – предоставить код, который должен быть выполнен, независимо от того, что еще выполнялось в блоке. Это может быть удаление динамических объектов, запись в журнал, закрытие выходных потоков, и тому подобное. Так как блок FINALLY исполняется, даже если было установлено состояние ERROR, он также полезен как часть структурной обработки ошибок. Так как он выполняется после блока CATCH, он может содержать общий пост-CATCH код.
Поведение блока FINALLY
Выполнение блока FINALLY
Блок FINALLY исполняется как конечный блок связанного блока. Блок FINALLY исполняется однократно в конце каждой итерации связанного блока. Блок FINALLY исполняется независимо от успешного или ошибочного исполнения связанного блока.
Видимость транзакций в блоке FINALLY
Транзакция связанного блока в момент исполнения блока FINALLY либо уже завершена (если ошибок не было), либо уже откачена (если ошибка возникла). Соответственно, любой оператор UNDO внутри блока FINALLY откатит только изменения, сделанные в этом блоке.
Блок FINALLY является откатываемым блоком с неявной директивой ON ERROR UNDO, THROW. Вы не можете явно изменить директиву ON ERROR для блока FINALLY. Если оператор внутри блока FINALLY устанавливает состояние ERROR, блок FINALLY будет откачен и ошибка будет передана для обработки блоку, охватывающему связанный блок. Ошибка не устанавливается в самом связанном блоке, так как это приведет к зацикливанию.
Другими словами, если оператор в блоке FINALLY устанавливает состояние ERROR, выполняются следующие действия:
- Откат (UNDO) блока FINALLY
- Выход (LEAVE) из связанного блока
- Передача (THROW) ошибки блоку, охватывающему связанный блок.
Те же действия выполняются при выполнении явного оператора THROW в блоке FINALLY.
Видимость буферов в блоке FINALLY
Буферы, область видимости которых совпадает со связанным блоком, будут недоступны при исполнении блока FINALLY. Это связано с тем, что в этот момент буфер либо уже откачен и освобожден (при ошибке) или сброшен в базу и освобожден (при успехе).
Если буфер, на который ссылается блок FINALLY, имеет ссылку на него вне связанного блока, его область видимости расширена до ближайшего блока, охватывающего все ссылки на буфер. Соответственно, такие буферы видимы в блоке FINALLY.
Передача управления в блоке FINALLY
Код в блоке FINALLY может содержать явные опции передачи управления (LEAVE, NEXT, RETRY, RETURN или THROW). Так как блок FINALLY является откатываемым, опции LEAVE, NEXT и RETRY без указания метки относятся к самому блоку FINALLY, а не к связанному блоку.
Если Вы хотите, чтобы они относились к связанному блоку, необходимо использовать метки. Опции перехода в блоке FINALLY отменяют “незавершенные” (pending) опции перехода в блоке CATCH.
Состояния STOP или QUIT и блоки FINALLY
Если в связанном блоке устанавливается состояние STOP или QUIT, блок FINALLY выполняться не будет, и будет выполнена обработка соответствующего состояния. Если связанный блок имеет фразу ON STOP или ON QUIT, то состояние STOP (QUIT) будет обработано и сброшено к тому моменту, когда нужно выполнять блок FINALLY – и блок FINALLY будет выполнен.
Примеры использования блока FINALLY
Приведенные примеры достаточно тривиальны, но иллюстрируют последовательность исполнения различных блоков.
Программа 55. Использование блока FINALLY – Пример 1
DO ON ERROR UNDO, LEAVE: FIND Customer 1000. /* Raises ERROR and execution goes to FINALLY block before the LEAVE option executes */ MESSAGE "This message never appears because of ERROR condition." VIEW-AS ALERT-BOX BUTTONS OK. FINALLY: MESSAGE "Inside FINALLY block." VIEW-AS ALERT-BOX BUTTONS OK. /* LEAVE DO block executes here */ END FINALLY. END. /* DO */ MESSAGE "Out of DO block." VIEW-AS ALERT-BOX BUTTONS OK.
При выполнении первого примера (Программа 55) происходит следующее:
- Оператор FIND вызывает состояние ERROR.
- Выполняется традиционная обработка ошибки в соответствии с фразой ON ERROR, явно указанной в заголовке оператора DO – выдается стандартное системное сообщение «** Customer record not on file. (138)» и выполняется откат (UNDO) блока DO. Отметим, что опция LEAVE, указанная во фразе ON ERROR в данный момент не выполняется.
- Выполняется блок FINALLY – и выдается сообщение из этого блока.
- По окончании блока FINALLY, так как в нем не содержится опций перехода, выполняется «отложенная» опция LEAVE, как указано в ON ERROR.
- Происходит выход из блока DO и выдается финальное сообщение.
Программа 56. Использование блока FINALLY – Пример 2
DO ON ERROR UNDO, LEAVE: FIND Customer 1000. /* Raises ERROR and execution goes to CATCH block. */ MESSAGE "This message never appears because of ERROR condition." VIEW-AS ALERT-BOX BUTTONS OK. CATCH eSysError AS Progress.Lang.SysError: /* Handler code for SysError condition */ MESSAGE "Inside CATCH block." VIEW-AS ALERT-BOX BUTTONS OK. /* Execution goes to FINALLY before leaving DO block. */ END CATCH. FINALLY: /* Your code */ MESSAGE "Inside FINALLY block." VIEW-AS ALERT-BOX BUTTONS OK. /* LEAVE DO block here. */ END FINALLY. END. /* DO */ MESSAGE "Out of DO block." VIEW-AS ALERT-BOX BUTTONS OK.
В примере (Программа 56) обработка ошибки осуществляется структурным блоком CATCH. Наличие блока CATCH подавляет выдачу системного сообщения об ошибке, и фраза ON ERROR в заголовке блока не используется. Исполняется блок CATCH (выдается сообщение), затем мы видим сообщение из блока FINALLY, и затем последнее сообщение из программы. Блоки CATCH и FINALLY не содержат опций перехода, фраза ON ERROR не используется, и выход из блока осуществляется естественным путем, так как блок не итерационный.
Программа 57. Использование блока FINALLY – Пример 3
DO ON ERROR UNDO, LEAVE: FIND Customer 1000. /* Raises ERROR and execution goes to the CATCH block*/ MESSAGE "This message never appears because of ERROR condition." VIEW-AS ALERT-BOX BUTTONS OK. CATCH eSysError AS Progress.Lang.SysError: /* Handler code for SysError condition */ MESSAGE "Inside CATCH block." VIEW-AS ALERT-BOX BUTTONS OK. /* Execution goes to FINALLY before leaving DO block. */ UNDO, THROW eSysError. END CATCH. FINALLY: /* Your code */ MESSAGE "Inside FINALLY block." VIEW-AS ALERT-BOX BUTTONS OK. END FINALLY. END. /* DO */ CATCH eSysError AS Progress.Lang.SysError: MESSAGE "Out of DO block and inside CATCH block for procedure block" VIEW-AS ALERT-BOX BUTTONS OK. END CATCH.
В примере (Программа 57) блок CATCH содержит оператор UNDO, THROW, который передает ошибку для обработки в охватывающий блок – в данном случае обработка выполняется в CATCH-блоке на уровне процедуры.
При возникновении ошибки, из-за наличия блока CATCH, выдача системного сообщения об ошибке подавляется. Выполняется блок CATCH в блоке DO, выдается сообщение, затем выполняется оператор UNDO, THROW, точнее, выполняется откат блока CATCH, выполнение THROW откладывается до исполнения блока FINALLY. Затем исполняется блок FINALLY. Он не содержит опций перехода, и по его завершении выполняется отложенная опция THROW, передающая управление в блок CATCH в процедуре.