Основы программирования — второй семестр 08-09; Михалкович С.С.; VII часть

Материал из Вики ИТ мехмата ЮФУ
Версия от 10:25, 21 мая 2009; Juliet (обсуждение | вклад) (Секции обработки исключений в блоке try)

Перейти к: навигация, поиск

Исключения

Введение. Что нам уже известно об исключениях

Впервые мы столкнулись с исключениями в начале курса, когда изучали оператор ввода и обработку ошибок ввода с помощью блока try..except.

Позже, при работе с файлами, возник термин «исключение». Мы узнали, как обрабатывать исключения с помощью оператора try..except и познакомились с оператором try..finally.

И, наконец, мы научились генерировать собственные исключения.

Теперь пришло время подробнее изучить исключения.

Иерархия исключений в .NET

Стандартные исключения связаны отношением наследования. Базовым классом для всех исключений является класс Exception, который находится в пространстве имен System.

<xh4> Классы исключений в .NET </xh4> Требуется подключить пространство имен System.

Exception
    ApplicationException
        Все пользовательские исключения
    SystemException
        AccessViolationException (несанкционированный доступ к памяти)
        ArgumentException
           ArgumentNullException
           ArgumentOutOfRangeException
        ArithmeticException
           DivideByZeroException (целочисленное деление на 0)
        IndexOutOfRangeException
        InvalidCastException (явное приведение к неправильному типу)
        FormatException 
        NullReferenceException
        OutOfMemoryException
        StackOverflowException
        KeyNotFoundException (пространство имен System.Collections.Generic)
        IOException (пространство имен System.IO)
           FileNotFoundException
           EndOfStreamException

Секции обработки исключений в блоке try

Полный синтаксис блока try выглядит так:

try
  ...
except
  on e: Exception1 do
    <оператор>;
  on e: Exception2 do
    <оператор>;
  ...
  [else <оператор>]
end;

Вопрос: надо ли обрабатывать все возникшие исключения?
Ответ: надо обрабатывать только те, которые понятно, как обрабатывать в данном месте программы. Остальные — не обрабатывать, а считать, что это должен сделать вызывающий код.

Пример 1.

procedure DoAnything(fName: string);
begin
  var f: text;
  assign(f, fName);
  reset(f);
  ...
end;

Вопрос: надо ли в данном контексте обрабатывать исключение FileNotFinfException?
Попробуем обработать:

try
  reset(f);
  ...
except
  // <ничего>
end;

Замечание. Обычно, тот, кто пишет код подпрограммы, которая может генерировать исключение, не знает, что с этими исключениями делать. А тот, кто вызывает — знает.
Поэтому обрабатывать исключение следует только в том месте, где уже известно, что надо делать (в нашем примере — в коде, вызывающем процедуру DoAnything).

Примечание. Раньше мы пользовались Assert:

var f: text;
Assert(FileExists(fName));
...

Недостаток этого способа состоит в том, что конкретное исключение FileNotFoundException заменяется на общее AssertionException, что плохо.

В более развитых средах, как мы уже знаем, вызов Assert не генерируется в версии Release, а только в версии Debug (при разработке). Поэтому в коде могут быть оставлены как Assert, так и обработчики специфических исключений (в версии Debug будет срабатывать Assert, а в версии Release — обработчик исключения).

Пример 2.

function MyMod(a, b: integer): integer;
begin
  Result := a - (a div b)*b;
end;

try
  readln(a, b);
  writeln(MyMod(a, b) div (a-1));
except
  on FormatException do
    ..
  on DivideByZeroException do
    ..
end;

Замечание. Деление на ноль может возникнуть дважды: в функции MyMod и в основной программе.
Вопрос: хотим ли мы отличать эти исключения?

Создание индивидуального класса исключения. Генерация исключений при обработке исключений