Оператор Попытка. Структурная обработка ошибок. — различия между версиями

Материал из ТХАБ.РФ
Перейти к: навигация, поиск
(Новая страница: «Говорят, что программ без ошибок не бывает. Оператор Попытка позволяет перехватить, проа…»)
 
м (Оператор ВызватьИсключение)
 
(не показаны 2 промежуточные версии этого же участника)
Строка 160: Строка 160:
 
  КонецПроцедуры     
 
  КонецПроцедуры     
  
Вывод:
+
'''Вывод:'''
 +
Оператор '''Попытка''' позволяет исключить крах программы при возникновении критических ошибок, возникших при выполнении программы.
  
Оператор Попытка позволяет исключить крах программы при возникновении критических ошибок, возникших при выполнении программы.
+
{{Описание языка Перфолента.NET-Содержание}}
  
 
== Ссылки ==
 
== Ссылки ==

Текущая версия на 12:06, 27 июля 2020

Говорят, что программ без ошибок не бывает. Оператор Попытка позволяет перехватить, проанализировать и обработать возникшие ошибки и предотвратить аварийное завершение программы.

При написании программ думайте о возможных ошибках. Проверяйте исходные данные.

Как бы мы не старались, но написать программу, в которой бы не случалось ничего не предвиденного невозможно. Впрочем, в обычной жизни без неожиданностей тоже не бывает. У кого-то даже может случиться инфаркт. Такой же «инфаркт» может случиться и в программе, в которой не предусмотрели обработку ошибочной ситуации.

Самый простой путь избежать ошибок в программе это проверка на корректность всех исходных данных. Если исходные данные корректны, то ошибок не произойдет, а если не корректны, то и выполнять действие с ними не надо. Однако, не все программисты одинаково профессиональны и внимательны. Что, если вы используете чужой код, а в нём возникает ошибка? Вот для этого случая в языке Перфолента.Net и предусмотрен оператор Попытка, который перехватывает случившиеся ошибки и позволяет избежать краха программы восстановив нормальное её выполнение.

Самый простой вариант оператора Попытка выглядит так:

Попытка
   //блок кода, в котором возможно возникновение ошибки
КонецПопытки    

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

А = 5
Б = 0
Попытка
   В = А / Б        //здесь произойдет деление на ноль
КонецПопытки    

Секция Исключение[править]

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

Попытка
   //блок кода, в котором возможно возникнет ошибка
Исключение
   //блок кода, в который мы попадем только при возникновении ошибки
КонецПопытки    

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

А = 5
Б = 0
Попытка
       В = А / Б        //здесь произойдет деление на ноль
Исключение
       ВыводСтроки "Произошло деление на ноль!"
КонецПопытки    

Отлично, однако в примере мы заранее знали какая ошибка произойдет, поэтому нам не составило труда написать сообщение об ошибке для пользователя. А что если характер ошибки нам не известен, т.к. она произошла внутри чужого кода? Тогда нам необходимо поймать ошибку в переменную и проанализировать её.

Попытка
       ВызовФункцииЧужойБиблиотеки()       //здесь может произойти не известная ошибка
Исключение Ош       //заведём переменную с именем Ош для перехвата ошибки
       ВыводСтроки "Произошла ошибка: "+Ош.ОписаниеОшибки()
КонецПопытки    

Переменная Ош будет иметь встроенный в язык тип Ошибка. А у этого типа есть метод ОписаниеОшибки, который возвращает строку с текстовым описанием произошедшей ошибки. Тест описания мог быть написан автором вызываемой библиотеки или автоматически составлен средой выполнения .Net.

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

Воспользуемся оператором Если для выяснения типа полученной ошибки.

Попытка
       ВызовФункцииЧужойБиблиотеки()       //здесь может произойти не известная ошибка
Исключение Ош       //заведём переменную с именем Ош для перехвата ошибки
       Если ТипЗнч(Ош)=Тип("ОшибкаВводаВывода")
               ВыводСтроки "Произошла ошибка ввода-вывода: "+Ош.ОписаниеОшибки()
       ИначеЕсли ТипЗнч(Ош)=Тип("ОшибкаЧтенияДанных")
               ВыводСтроки "Произошла ошибка чтения данных: "+Ош.ОписаниеОшибки()
       Иначе
               ВыводСтроки "Произошла не известная ошибка: "+Ош.ОписаниеОшибки()
       КонецЕсли
КонецПопытки    

Если присмотреться, то можно заметить, что оператор Выбор Для подойдет для этого случая лучше.

Попытка
       ВызовФункцииЧужойБиблиотеки()       //здесь может произойти не известная ошибка
Исключение Ош       //заведём переменную с именем Ош для перехвата ошибки
       Выбор Для Ош
       Когда ЭтоТип ОшибкаВводаВывода
               ВыводСтроки "Произошла ошибка ввода-вывода: "+Ош.ОписаниеОшибки
       Когда ЭтоТип ОшибкаЧтенияДанных
               ВыводСтроки "Произошла ошибка чтения данных: "+Ош.ОписаниеОшибки
       Иначе
               ВыводСтроки "Произошла не известная ошибка: "+Ош.ОписаниеОшибки
       КонецВыбора
КонецПопытки    

Обратите внимание, что в трех последних примерах ошибка произошла где-то в коде метода ВызовФункцииЧужойБиблиотеки(), который нам не доступен для изучения. Ошибка не была перехвачена внутри метода и была передана вызывающему коду, т.е. нашему методу, где мы её и перехватили. Если бы мы её не перехватили, то управление было бы немедленно передано коду, вызвавшему наш метод, и ошибка была бы передана уже туда. Ошибка, не перехваченная ни где, приведет к немедленному краху программы и закрытию её операционной системой.

Секция Завершение.[править]

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

Гарантированное выполнение завершающих действий можно произвести в не обязательной секции Завершение оператора Попытка.

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

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

Накопитель = Новый НакопительДанных
Попытка
       Накопитель.Открыть()
       Пока Накопитель.ЕстьДанные
               //на какой-либо итерации цикла может произойти ошибка
               //однако, не хотелось бы потерять уже накопленные данные 
              Накопитель.ПолучитьДанные() 
       КонецЦикла   
       Возврат    //возврат не помешает выполнению кода в секции Завершение    
Исключение Ош
       //сохраним сообщение об ошибке для будущего анализа
       Лог.ЗаписатьСообщениеОбОшибке(Ош.ОписаниеОшибки())
       //отправим ошибку внешнему вызывающему коду
       //это не помешает выполнению кода в секции Завершение
       ВызватьИсключение "Не удалось прочитать все данные из-за ошибки: "+ Ош.ОписаниеОшибки()       
Завершение
       //сохраним данные в любом случае
       //была ошибка или нет
       Накопитель.СохранитьВФайл("МойФайл")
       //закроем накопитель и освободим ссылку на него
       Накопитель.Закрыть() 
       Накопитель = Неопределено
КонецПопытки

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

Попытка
       //сделаем ошибку
       А = 5 / 0
       ВыводСтроки "Этот текст не будет выведен!"
Завершение
       ВыводСтроки "Завершение без секции Исключение... после ошибки..."
КонецПопытки

Оператор ВызватьИсключение[править]

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

Для передачи внешнему коду информации о произошедшей ошибке служит оператор ВызватьИсключение.

Существует 3 варианта оператора ВызватьИсключение.

//вызов исключения без параметров
ВызватьИсключение 
//вызов исключения с параметром типа Строка
ВызватьИсключение "Моё сообщение об ошибке" 
//вызов исключения с параметром типа Ошибка
ВызватьИсключение Новый Ошибка("Моё сообщение об ошибке")

Пример вызова исключения в случае, когда от внешнего кода получены ошибочные исходные данные:

Процедура ОбработатьСтроку(ВходящаяСтрока тип Строка)
       Если ВходящаяСтрока Это Неопределено
               ВызватьИсключение "Не задана строка для обработки!"
       КонецЕсли
       //код обработки строки
КонецПроцедуры    
Вывод:
Оператор Попытка позволяет исключить крах программы при возникновении критических ошибок, возникших при выполнении программы.
'Показать Содержание Описание языка Перфолента.NET'

Ссылки[править]