Блок try..Except
Для реакции на конкретный тип ситуации применяется блок try..except. Синтаксис его следующий:
try
<Оператор>;
<Оператор>;
...
except
on EException1 do
<Оператор>; {обработчик ИС типа EException1}
on EException2 do
<Оператор>;
...
else
<0ператор> {обработчик прочих ИС}
end;
Выполнение блока начинается с секции try. При отсутствии исключительных ситуаций только она и выполняется. Секция except получает управление в случае возникновения ИС. После обработки происходит выход из защищенного блока, и управление обратно в секцию try не передается; выполняются операторы, стоящие после end.
Если вы хотите обработать любую ИС одинаково, независимо от ее класса, вы можете писать код прямо между операторами except и end. Но если обработка отличается, здесь можно применять набор директив on…do, определяющих реакцию приложения на определенную ситуацию. Каждая директива связывает ситуацию (on...), заданную своим именем класса, с группой операторов (do...).
U := 220.0;
R := 0;
try
I := U / R;
except
on EZeroDivide do MessageBox('Короткое замыкание!');
end;
В этом примере замена if…then на try…except, может быть, не дала очевидной экономии кода. Однако если при решении, допустим, вычислительной задачи проверять на возможное деление на ноль приходится не один, а множество раз, то выигрыш от нового подхода неоспорим - достаточно одного блока try…except на все вычисления.
При возникновении ИС директивы просматриваются последовательно, в порядке их описания. Каждый тип исключительной ситуации, описанный после ключевого слова on, обрабатывается именно этим блоком: только то, что предусмотрено в нем, и будет являться реакцией на данную ситуацию.
Если при этом обработчик родительского класса стоит перед дочерним, последний никогда не получит управления.
try
i:=l;j:=0;
k:=i div j;
except
on EIntError do ShowMessage('IntError');
on EDivByZero do ShowMessage('DivByZero');
end;
В этом примере, хотя в действительности будет иметь место деление на ноль (EDivByZero), вы увидите сообщение, соответствующее родительскому классу EIntError. Но стоит поменять две конструкции on…do местами, и все придет в норму.
Если возникла ситуация, не определенная ни в одной из директив, выполняются те операторы, которые стоят после ключевого слова else. Если и их нет, то ИС считается не обработанной и будет передана на следующий уровень обработки. Этим следующим уровнем может быть другой оператор try..except, который содержит в себе данный блок.
Блок try...Finally
Параллельно с блоком try..except в языке существует и try. .finally. Он соответствует случаю, когда необходимо возвратить выделенные программе ресурсы даже в случае аварийной ситуации. Синтаксис блока try..finally таков:
try
<Оператор>
<Оператор>
...
finally
<Оператор>
...
end;
Смысл этой конструкции можно описать одним предложением: операторы, стоящие после finally, выполняются всегда.
Следующие за try операторы исполняются в обычном порядке. Если за это время не возникло никаких ИС, далее следуют те операторы, которые стоят после finally. В случае, если между try и finally произошла ИС, управление немедленно передается на операторы после finally, которые называются кодом очистки. Допустим, вы поместили после try операторы, которые должны выделить вам ресурсы системы (дескрипторы блоков памяти, файлов, контекстов устройств и т. п.). Тогда операторы, освобождающие их, следует поместить после finally, и ресурсы будут освобождены в любом случае. Блок try...finally, как можно догадаться, еще называется блоком защиты ресурсов.
Важно обратить внимание на такой факт: данная конструкция ничего не делает с самим объектом — исключительной ситуацией. Задача try...finally — только прореагировать на факт нештатного поведения программы и проделать определенные действия. Сама же ИС продолжает "путешествие" и вопрос ее обработки остается на повестке дня.
Блоки защиты ресурсов и обработчики ИС, как и другие блоки, могут быть вложенными. В этом простейшем примере каждый вид ресурсов системы защищается в отдельном блоке:
try
AllocatelstResource;
try
Allocate2ndResource;
SolveProblem;
finally
Free2ndResource;
end;
finally
FreelstResource;
end;
Можно также вкладывать обработчики друг в друга, предусмотрев в каждом специфическую реакцию на ту или иную ошибку:
var i,j,k : Integer;
begin
i := Round(Random);
j := 1 - i;
try
k := 1 div i;
try
k := 1 div j;
except
On EDivByZero do
ShowMessage('Вариант 1: j=0');
end;
except
On EDivByZero do
ShowMessage('Вариант 2: i=0');
end;
end;
Но все же идеально правильный случай — это сочетание блоков двух типов. В один из них помещается общее (освобождение ресурсов в finally), в другой - особенное (конкретная реакция внутри except).
