13 · Обработка ошибок
Оператор TRY
Недоступный файл, аргумент вне допустимого диапазона, деление на ноль — такой отказ не всегда должен останавливать программу. Часто достаточно пропустить неудавшуюся операцию, записать сведения и перейти к следующему шагу. TRY размечает участок, ошибки внутри которого программа обрабатывает сама: блок CATCH получает возникшую ошибку, блок FINALLY выполняет завершающие инструкции.
Когда нужен TRY
Обычное применение оператора — обработка ожидаемого отказа: файл не удалось открыть, аргумент функции оказался неподходящим. Вместо аварийного завершения программа получает управление в обработчике и продолжает работу после END TRY. TRY доступен на верхнем уровне и внутри подпрограмм.
Перехватываются только ошибки времени выполнения. Ошибки лексического, синтаксического и семантического анализа обнаруживаются до запуска и этим оператором не перехватываются.
Формы записи
Запись содержит защищённый участок и хотя бы один из двух блоков — обработчик CATCH или завершающий блок FINALLY. Защищённые инструкции выполняют основную операцию. Имя после CATCH объявляет доступную только для чтения переменную типа ERROR, а инструкции обработки выполняются, если ошибка пришла из защищённого участка. Завершающие инструкции выполняются после обычного завершения, после обработки ошибки или при передаче неперехваченной ошибки дальше. Набор блоков и различает три формы.
Обработчик без завершающего блока:
TRY
<защищённые инструкции>
CATCH <имя ошибки>
<инструкции обработки>
END TRYЗавершающий блок без обработчика:
TRY
<защищённые инструкции>
FINALLY
<завершающие инструкции>
END TRYОба блока вместе:
TRY
<защищённые инструкции>
CATCH <имя ошибки>
<инструкции обработки>
FINALLY
<завершающие инструкции>
END TRYВ последней форме обработчик получает ошибку, а завершающий блок выполняет то, что должно произойти в любом случае. Программа ниже показывает порядок:
TRY
PRINT 10 \ 0
CATCH err
PRINT err.Code
FINALLY
PRINT "finally"
END TRY
PRINT "after"Ожидаемый вывод:
MB3001
finally
afterДеление на ноль даёт MB3001, и обработчик печатает код. После обработчика выполняется FINALLY, затем управление выходит за END TRY и печатает after. Ошибочная инструкция при этом не повторяется.
Объявление переменной обработчика
В записи CATCH err слово CATCH вводит обработчик, а имя err объявляет его переменную. Объявление уже присутствует в этой строке, поэтому писать предварительно DIM err или CONST err не нужно. Тип ERROR задаётся самой конструкцией, и AS ERROR после имени не указывается. Имя выбирает программист по обычным правилам: например, CATCH failure объявляет переменную failure.
Переменная доступна только для чтения: программа получает сведения о возникшей ошибке и может их прочитать, но не может присвоить новое значение ни самой переменной, ни её полям. Константой оператора CONST она при этом не является, потому что сведения поступают в неё во время выполнения, при переходе в обработчик. Подсказка IDE обозначает её как «переменная ошибки, только для чтения» и показывает тип ERROR.
Область видимости начинается в блоке соответствующего CATCH и заканчивается перед FINALLY или END TRY. Во вложенных блоках обработчика переменная доступна, а в защищённых инструкциях TRY, в соседнем блоке FINALLY и после END TRY — нет. Повторно объявить это имя в той же области или перекрыть им доступное внешнее имя нельзя.
Тип ERROR и сведения об ошибке
ERROR — встроенный тип значения ошибки. Четыре его поля заранее определены языком, и программист не объявляет их ни через DIM, ни через TYPE. При перехвате ошибки переменная обработчика получает значение с кодом, сообщением и местом отказа.
Поля читаются через точку. В выражении err.Code имя переменной — err, точка обозначает обращение к полю, а Code — имя поля; вся запись является выражением чтения поля, а не одним именем с точкой. Для переменной failure те же поля записываются как failure.Code, failure.Message, failure.Line и failure.Column.
| Поле | Тип | Назначение |
|---|---|---|
| Code | STRING | Код системной или пользовательской ошибки. |
| Message | STRING | Английский текст системной ошибки либо исходный текст пользовательского сообщения ERROR. |
| Line | INTEGER | Номер строки исходника начиная с 1; 0, если место неизвестно. |
| Column | INTEGER | Номер столбца исходника начиная с 1; 0, если место неизвестно. |
Чтение полей на примере
TRY
PRINT POW(-2, 0.5)
CATCH err
PRINT err.Code
PRINT err.Message
PRINT err.Line
PRINT err.Column
FINALLY
PRINT "Завершено"
END TRYВызов POW(-2, 0.5) вызывает ошибку MB3007. Строка CATCH err объявляет переменную err; следующие четыре инструкции читают её поля. Первая выводит MB3007, вторая — сообщение операции, третья и четвёртая — строку и столбец места ошибки. Затем блок FINALLY выводит «Завершено». Дополнительное объявление err в этой программе отсутствует, потому что её уже объявил CATCH.
Если нужно изменять собственный текст сообщения, объявите отдельную строковую переменную внутри обработчика и скопируйте в неё err.Message. Запись err.Message = "другой текст" запрещена.
Язык поля Message
Системное сообщение в поле Message формируется на английском языке независимо от выбранного языка интерфейса IDE и культуры выполнения программы. Например, ошибка POW(-2, 0.5) возвращает Function POW: argument 'value' cannot be used with the specified exponent.. Слово value в кавычках обозначает имя аргумента функции POW, в данном случае её основание -2.
Поле Message и сообщение на панели «Проблемы» имеют разные назначения: диагностика IDE может отображаться на языке интерфейса, а программа получает английский текст системного отказа. Для отказов без специального английского описания используется общее английское описание по коду ошибки с названием операции и доступными именами или путями из контекста; подробный исходный текст используется в диагностике неперехваченной ошибки. Перехваченная ошибка снимается с панели «Проблемы». Имена и пути являются данными программы и могут содержать символы других алфавитов.
Сообщение, явно переданное оператору ERROR, сохраняется без перевода, включая русские символы и пустую строку. Это правило действует и при пользовательском коде, совпадающем с системным, например ERROR "MB3007", "Моя ошибка". Если сообщение не задано, оператор создаёт английский текст User error <код>.. Повторная передача ERROR err сохраняет поле Message исходной ошибки.
Запускаемый пример: язык системного и пользовательского сообщения. Для выбора обработки используйте поле Code, а не поиск слов в тексте Message.
Порядок работы и ограничения
При ошибке оставшиеся инструкции защищённого участка пропускаются. Если CATCH завершился обычно, исполнение продолжается после FINALLY и END TRY. Ошибка внутри CATCH не перехватывается тем же CATCH: сначала выполняется FINALLY, затем ищется внешний обработчик. Ошибка из FINALLY также передаётся внешнему обработчику.
RETURN из защищённого тела или CATCH выполняет все активные FINALLY, начиная с ближайшего и заканчивая внешним; каждый из них выполняется ровно один раз. Обработчик без FINALLY не мешает выполнению внешнего FINALLY. Если RETURN находится внутри самого FINALLY, этот FINALLY повторно не запускается.
Блоки можно вкладывать, и каждый имеет собственную область видимости. Допускается только один CATCH, без фильтра по коду или типу. Продолжение с места отказа не предусмотрено.
Диагностика
MB1001 возникает, если отсутствует требуемый CATCH или FINALLY, имя после CATCH либо END TRY. MB2010 возникает при попытке присвоить значение переменной ошибки или её полям. MB2001 возникает при обращении к переменной обработчика вне её области видимости. MB2002 означает повторное объявление имени в той же области, MB2016 — перекрытие доступного внешнего имени. MB2023 возникает при обращении к отсутствующему полю ERROR, например err.Text вместо err.Message. Коды перехваченных ошибок задаются самой операцией; например, деление на ноль даёт MB3001.