Обработка исключений: обзор
Когда во время выполнения программы происходит ошибка,
генерируется так называемое исключение, которое можно перехватить
и обработать. Исключение представляет собой объект класса, производного от
класса Exception
, создающийся при
возникновении исключительной ситуации.
Имеется ряд стандартных типов исключений. Можно также определять пользовательские типы исключений.
Если исключение не обработать, то программа завершится с
ошибкой. Для обработки исключений используется оператор
try
... except
.
Обычно исключения возбуждаются в подпрограммах, поскольку разработчик подпрограммы, как правило, не знает, как обработать ошибочную ситуацию. В месте вызова подпрограммы уже, как правило, известно, каким образом следует обрабатывать исключение. Например, пусть разработана следующая функция:
function mymod(a,b: integer): integer;
begin
Result := a - (a div b) * b;
end;
Если вызвать mymod(1,0)
, то
будет возбуждено исключение System.DivideByZeroException
целочисленного деления на 0.
Рассмотрим наивную попытку обработать ошибочную ситуацию
внутри функции mymod
:
function mymod(a,b: integer): integer;
begin
if b = 0 then
writeln('Функция mymod: деление на 0');
Result := a - (a div b) * b;
end;
Подобное решение является плохим, поскольку программист,
разрабатывающий функцию mymod
, не знает, как
она будет использоваться. Например, при вызове функции mymod
в цикле мы увидим на экране многократное сообщение об ошибке.
Простейший способ - оставить исходный вариант функции и
обрабатывать исключение System.DivideByZeroException
:
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
...
except
on System.DivideByZeroException do
writeln('Деление на 0');
end;
Отличие от вывода внутри функции состоит в том, что при разработке программы мы сами определяем действие, которое необходимо выполнять при обработке исключения. Это может быть специфическое сообщение об ошибке, вывод в файл ошибок или пустой оператор (в случае, когда требуется "беззвучно" погасить исключение).
Однако, данное решение обладает существенным недостатком:
исключение System.DivideByZeroException
будет возбуждено и
при a=1 и не будет связано с функцией mymod
.
Для устранения подобного недостатка определим собственный класс исключения и
возбудим его в функции mymod
:
type MyModErrorException = class(System.Exception) end;
function mymod(a,b: integer): integer;
begin
if b = 0 then
raise new MyModErrorException('Функция mymod: деление на 0');
Result := a - (a div b) * b;
end;
Тогда обработка ошибок будет выглядеть так:
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
...
except
on System.DivideByZeroException do
writeln('Деление на 0');
on e: MyModErrorException do
writeln(e.Message);
else writeln('какое-то другое исключение')
end;
Если сделать MyModErrorException
наследником класса System.ArithmeticException
, как и
System.DivideByZeroException
, то последний код можно упростить:
type MyModErrorException = class(System.ArithmeticException) end;
...
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
...
except
on e: System.ArithmeticException do
writeln(e.Message);
else writeln('Какое-то другое исключение')
end;
Наконец, можно поступить следующим образом.
Перехватим в функции mymod
исключение System.DivideByZeroException
и в
ответ сгенерируем новое - MyModErrorException
:
function mymod(a,b: integer): integer;
begin
try
Result := a - (a div b) * b;
except
on e: System.DivideByZeroException do
raise new MyModErrorException('Функция mymod: деление на 0');
end;
end;