Главная страница
Навигация по странице:

  • Класс System.Exception

  • Основы обработки исключений

  • Использование try- и catch-блоков

  • Пример обработки исключения

  • Второй пример исключения

  • Последствия возникновения неперехватываемых исключений

  • Возможность красиво выходить из ошибочных ситуаций

  • Использование нескольких catch-инструкций

  • Справочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией


    Скачать 5.05 Mb.
    НазваниеСправочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией
    АнкорC #.pdf
    Дата08.12.2017
    Размер5.05 Mb.
    Формат файлаpdf
    Имя файлаC #.pdf
    ТипСправочник
    #10795
    страница22 из 52
    1   ...   18   19   20   21   22   23   24   25   ...   52
    Глава 13
    Обработка исключительных
    ситуаций

    350
    Часть I. Язык C#
    сключительная ситуация (или исключение) — это ошибка, которая возникает во время выполнения программы.
    Используя C#-подсистему обработки исключительных ситуаций, с такими ошибками можно справляться. Эта подсистема в C# включает в себя усовершенствованные методы, используемые в языках C++ и Java.
    Поэтому эта тема будет знакомой для C++- и Java-программистов. Однако обработка исключений в C# отличается ясностью и полнотой реализации.
    Преимущество подсистемы обработки исключений состоит в автоматизации создания большей части кода, который ранее необходимо было вводить в программы “вручную”.
    Например, в любом компьютерном языке при отсутствии такой подсистемы практически каждый метод возвращал коды ошибок, и эти значения проверялись вручную при каждом вызове метода. Такой подход довольно утомителен, кроме того, при этом возможно возникновение ошибок. Обработка исключений упрощает “работу над ошибками”, позволяя в программах определять блок кола, именуемый обработчиком исключений, который будет автоматически выполняться при возникновении определенной ошибки. В этом случае не обязательно проверять результат выполнения каждой конкретной операции или метода вручную. Если ошибка возникнет, ее должным образом обработает обработчик исключений.
    Еще одним преимуществом обработки исключительных ситуаций в C# является определение стандартных исключений для таких распространенных программных ошибок, как деление на нуль или попадание вне диапазона определения индекса. Чтобы отреагировать на возникновение таких ошибок, программа должна отслеживать и обрабатывать эти исключения.
    Без знания возможностей C#-подсистемы обработки исключений успешное программирование на C# попросту невозможно.
    Класс System.Exception
    В C# исключения представляются классами. Все классы исключений должны быть выведены из встроенного класса исключений
    Exception
    , который является частью пространства имен
    System
    . Таким образом, все исключения — подклассы класса
    Exception
    Из класса
    Exception выведены классы
    SystemException и
    ApplicationException
    . Они поддерживают две общие категории исключений, определенные в C#: те, которые генерируются C#-системой динамического управления, или общеязыковым средством управления (Common Language Runtime — CLR), и те, которые генерируются прикладными программами. Но ни класс
    SystemException
    , ни класс
    ApplicationException не привносят ничего нового в дополнение к членам класса
    Exception
    . Они просто определяют вершины двух различных иерархий классов исключений.
    C# определяет встроенные исключения, которые выводятся из класса
    SystemException
    . Например, при попытке выполнить деление на нуль генерируется исключение класса
    DivideByZeroException
    . Как будет показано ниже в этой главе, вы сможете создавать собственные классы исключений, выводя их из класса
    ApplicationException
    Основы обработки исключений
    Управление C#-механизмом обработки исключений зиждется на четырех ключевых словах: try
    , catch
    , throw и finally
    . Они образуют взаимосвязанную подсистему,
    И

    Глава 13. Обработка исключительных ситуаций
    351 в которой использование одного из них предполагает использование другого. В этой главе каждое слово рассматривается подробно. Однако для начала будет полезно получить общее представление о роли, которую они играют в обработке исключительных ситуаций. Если кратко, то их работа состоит в следующем.
    Программные инструкции, которые нужно проконтролировать на предмет исключений, помещаются в try
    -блок. Если исключение таки возникает в этом блоке, оно дает знать о себе выбросом определенного рода информации. Это выброшенное исключение может быть перехвачено программным путем с помощью catch
    -блока и обработано соответствующим образом. Системные исключения автоматически генерируются C#-системой динамического управления. Чтобы сгенерировать исключение вручную, используется ключевое слово throw
    . Любой код, который должен быть обязательно выполнен при выходе из try
    -блока, помещается в блок finally
    Использование try- и catch-блоков
    Ядром обработки исключений являются блоки try и catch
    . Эти ключевые слова работают “в одной связке”; нельзя использовать слово try без catch или catch без try
    Вот каков формат записи try/catch
    -блоков обработки исключений: try {
    //
    Блок кода, подлежащий проверке на наличие ошибок.
    } catch(
    ExcepType1
    exOb
    ) {
    //
    Обработчик для исключения типа ExcepType1.
    } catch(
    ExcepType2
    exOb
    ) {
    //
    Обработчик для исключения типа ExcepType2.
    }...
    Здесь
    ЕхсерТуре
    — это тип сгенерированного исключения. После “выброса” исключение перехватывается соответствующей инструкцией catch
    , которая его обрабатывает. Как видно из формата записи try/catch
    -блоков, с try
    -блоком может быть связана не одна, а несколько catch
    -инструкций. Какая именно из них будет выполнена, определит тип исключения. Другими словами, будет выполнена та catch
    -инструкция, тип исключения которой совпадает с типом сгенерированного исключения (а все остальные будут проигнорированы). После перехвата исключения параметр
    exOb
    примет его значение.
    Задавать параметр
    exOb
    необязательно. Если обработчику исключения не нужен доступ к объекту исключения (как это часто бывает), в задании параметра
    exOb
    нет необходимости. Поэтому во многих примерах этой главы параметр
    exOb
    не задан.
    Важно понимать следующее. Если исключение не генерируется, try
    -блок завершается нормально, и все его catch
    -инструкции игнорируются. Выполнение программы продолжается с первой инструкции, которая стоит после последней инструкции catch
    . Таким образом, catch
    -инструкция (из предложенных после try
    -блока) выполняется только в случае, если сгенерировано соответствующее исключение.
    Пример обработки исключения
    Ниже приведен простой пример, демонстрирующий, как отследить и перехватить исключение. Известно, что попытка индексировать массив за пределами его границ вызывает ошибку нарушения диапазона. В этом случае C#-система динамического управления генерирует исключение типа
    IndexOutOfRangeException
    , которое представляет собой стандартное исключение, определенное языком C#. В следующей программе такое исключение намеренно генерируется, а затем перехватывается.

    352
    Часть I. Язык C#
    // Демонстрация обработки исключений. using System; class ExcDemo1 { public static void Main() { int[] nums = new int[4]; try
    {
    Console.WriteLine(
    "Перед генерированием исключения.");
    //
    Генерируем исключение, связанное с попаданием
    // индекса вне диапазона. for(int i=0; i < 10; i++) { nums[i]
    = i;
    Console.WriteLine("nums[ {0}] : {1}", i, nums[i]);
    }
    Console.WriteLine("Этот текст не отображается.");
    } catch(IndexOutOfRangeException) {
    //
    Перехватываем исключение-
    Console.WriteLine("Индекс вне диапазона!");
    }
    Console.WriteLine("После catch-инструкции.");
    }
    }
    При выполнении этой программы получаем такие результаты:
    Перед генерированием исключения. nums[0]: 0 nums[1]: 1 nums[2]: 2 nums[3]: 3
    Индекс вне диапазона!
    После catch-инструкции.
    Обратите внимание на то, что nums
    — это int
    -массив для хранения четырех элементов. Однако в цикле for делается попытка индексировать этот массив от 0 до 9, и как только значение индекса устанавливается равным четырем, генерируется исключение типа
    IndexOutOfRangeException
    Несмотря на небольшой размер, предыдущая программа иллюстрирует ряд ключевых аспектов обработки исключений. Во-первых, проверяемый код содержится внутри try
    - блока. Во-вторых, при возникновении исключения (в данном случае из-за попытки внутри for
    -цикла индексировать массив nums за границами его диапазона) выполнение try
    - блока прекращается, а само исключение перехватывается catch
    -инструкцией. Другими словами, управление программой передается catch
    -инструкции, независимо от того, все ли инструкции try
    -блока выполнились. При этом важно то, что инструкция catch
    не
    вызывается, а ей передается управление программой. Поэтому инструкция
    Console.WriteLine("После catch-инструкции."}; никогда не выполнится. После выполнения catch
    -инструкции программа продолжится со следующей инструкции. Следовательно, чтобы ваша программа могла нормально продолжить свое выполнение, обработчик должен устранить проблему, которая стала причиной возникновения исключительной ситуации.

    Глава 13. Обработка исключительных ситуаций
    353
    Обратите внимание на то, что в инструкции catch параметр отсутствует. Как упоминалось выше, параметр необходим только в тех случаях, когда требуется доступ к объекту исключения. В некоторых случаях значение объекта исключения используется обработчиком для получения дополнительной информации об ошибке, но чаще всего достаточно просто знать о том, что исключение имело место. Следовательно, в отсутствии catch
    -параметра в обработчике исключения нет ничего необычного, как в случае, проиллюстрированном предыдущей программой.
    Как уже упоминалось, если try
    -блоком исключение не сгенерировано, ни одна из catch
    -инструкций не выполняется, и управление программой будет передано инструкции, следующей за catch
    -инструкцией. Чтобы убедиться в этом, замените в предыдущей программе эту инструкцию for
    -цикла for(int i=0; i < 10; i++) { такой: for(int i=0; i < nums.Length; i++) {
    Теперь цикл for не нарушает границы индексирования массива nums
    . Поэтому исключение не генерируется, и catch
    -блок не выполняется.
    Второй пример исключения
    Весь код, выполняемый внутри try
    -блока, проверяется на предмет возникновения исключительной ситуации. Сюда также относятся исключения, которые могут сгенерировать методы, вызываемые из блока try
    . Исключение, сгенерированное методом, вызванным из try
    -блока, может быть перехвачено этим try
    -блоком, если, конечно, метод сам не перехватит это исключение. Рассмотрим пример.
    /* Исключение может сгенерировать один метод, а перехватить его — другой. */ using System; class ExcTest {
    //
    Генерируем исключение. public static void genException() { int[] nums = new int[4];
    Console.WriteLine("Перед генерированием исключения.");
    //
    Генерируем исключение, связанное с попаданием
    // индекса вне диапазона. for(int i=0; i < 10; i++) { nums[i]
    = i;
    Console.WriteLine("nums[{0}]: {1}", i, nums[i]);
    }
    Console.WriteLine("Этот текст не будет отображаться.");
    }
    } class ExcDemo2 { public static void Main() { try
    {
    ExcTest.genException();
    } catch(IndexOutOfRangeException)
    {

    354
    Часть I. Язык C#
    //
    Перехватываем исключение.
    Console.WriteLine("Индекс вне диапазона!");
    }
    Console.WriteLine("После catch-инструкции.");
    }
    }
    Эта программа показывает результаты, которые не отличаются от результатов выполнения предыдущей ее версии:
    Перед генерированием исключения. nums[0]: 0 nums[1]: 1 nums[2]: 2 nums[3]: 3
    Индекс вне диапазона!
    После catch-инструкции.
    Поскольку метод genException()
    вызывается из блока try
    , исключение, которое он генерирует (и не перехватывает), перехватывается инструкцией catch в методе
    Main()
    . Но если бы метод genException()
    перехватывал это исключение, оно бы никогда не вернулось в метод
    Main()
    Последствия возникновения неперехватываемых
    исключений
    Перехват одного из стандартных C#-исключений, как показала предыдущая программа, имеет побочный эффект: он предотвращает аварийное окончание программы.
    При генерировании исключения оно должно быть перехвачено программным кодом. Если программа не перехватывает исключение, оно перехватывается C#-системой динамического управления. Но дело в том, что система динамического управления сообщит об ошибке и завершит программу. Например, в следующем примере исключение, связанное с нарушением границ диапазона, программой не перехватывается.
    // Предоставим возможность обработать ошибку
    // C#-системе динамического управления. using System; class NotHandled { public static void Main() { int[] nums = new int[4];
    Console.WriteLine("Перед генерированием исключения.");
    //
    Генерируем исключение, связанное с попаданием
    // индекса вне диапазона. for(int i=0; i < 10; i++) { nums[i]
    = i;
    Console. WriteLine("nums[{0} ] : {1}", i, nums[i]);
    }
    }
    }
    При неверном индексировании массива выполнение программы останавливается, и на экране отображается следующее сообщение об ошибке:

    Глава 13. Обработка исключительных ситуаций
    355
    Unhandled Exception: System.IndexOutOfRangeException:
    Index was outside the bounds of the array. at NotHandled.Main()
    Это сообщение уведомляет об обнаружении в методе
    NotHandled.Main()
    необработанного исключения типа
    System.IndexOutOfRangeException
    , которое связано с выходом индекса массива за границы диапазона.
    Несмотря на то что такое сообщение может быть полезным во время отладки программы, вряд ли вы захотите, чтобы его увидели пользователи! Поэтому важно, чтобы программы сами обрабатывали подобные исключения.
    Как упоминалось выше, тип исключения должен совпадать с типом, заданным в catch
    -инструкции. В противном случае это исключение не будет перехвачено. Например, в следующей программе делается попытка перехватить ошибку нарушения индексом массива границ диапазона с помощью catch
    -инструкции для класса
    DivideByZeroException
    (это еще одно из встроенных C#-исключений). При нарушении границ диапазона, допустимого для индекса массива, генерируется исключение типа
    IndexOutOfRangeException
    , которое не перехватывается предусмотренной в
    программе catch
    -инструкцией. В результате программа завершается аварийно.
    // Эта программа работать не будет! using System; class ExcTypeMismatch { public static void Main() { int[] nums = new int[4]; try
    {
    Console.WriteLine("Перед генерированием исключения.");
    //
    Генерируем исключение, связанное с попаданием
    // индекса вне диапазона. for(int i=0; i < 10; i++) { nums[i]
    = i;
    Console.WriteLine("nums[{0}]: {1}", i, nums[i]);
    }
    Console.WriteLine("Этот текст не отображается.");
    }
    /*
    Если в catch-инструкции указан тип исключения
    DivideByZeroException, то с ее помощью невозможно перехватить ошибку нарушения границ массива. */ catch(DivideByZeroException) {
    //
    Перехватываем исключение.
    Console.WriteLine("Индекс вне границ диапазона!");
    }
    Console.WriteLine("После catch-инструкции.");
    }
    }
    Вот как выглядят результаты выполнения этой программы:
    Перед генерированием исключения. nums[0]: 0 nums[1]: 1 nums[2]: 2 nums[3]: 3

    356
    Часть I. Язык C#
    Unhandled Exception: System.IndexOutOfRangeException:
    Index was outside the bounds of the array. at ExcTypeMismatch.Main()
    Как подтверждают результаты выполнения этой программы, catch
    -инструкция, предназначенная для перехвата исключения типа
    DivideByZeroException
    , не в состоянии перехватить исключение типа
    IndexOutOfRangeException
    Возможность красиво выходить из ошибочных
    ситуаций
    Одно из основных достоинств обработки исключений состоит в том, что она позволяет программе отреагировать на ошибку и продолжить выполнение. Рассмотрим, например, следующую программу, которая делит элементы одного массива на элементы другого. Если при этом встречается деление на нуль, генерируется исключение типа
    DivideByZeroException
    . В программе это исключение обрабатывается выдачей сообщения об ошибке, после чего выполнение программы продолжается. Следовательно, попытка разделить на нуль не вызывает внезапную динамическую ошибку, в результате которой прекращается выполнение программы. Вместо аварийного останова исключение позволяет красиво выйти из ошибочной ситуации и продолжить выполнение программы.
    // Достойная реакция на ошибку и продолжение работы --
    // вот что значит с толком использовать исключения! using System; class ExcDemo3 { public static void Main() { int[] numer = { 4, 8, 16, 32, 64, 128 }; int[] denom = { 2, 0, 4, 4, 0, 8 }; for(int i=0; i < numer. Length; i++) { try
    {
    Console.WriteLine(numer[i] + " / " + denom[i]
    +
    " равно " + numer[i]/denom[i]);
    } catch(DivideByZeroException)
    {
    // Перехватываем исключение.
    Console.WriteLine("Делить на нуль нельзя!");
    }
    }
    }
    }
    При выполнении эта программа демонстрирует следующие результаты:
    4/2 равно 2
    Делить на нуль нельзя!
    16/4 равно 4 32/4 равно 8
    Делить на нуль нельзя!
    128 / 8 равно 16
    Эта программа демонстрирует еще один важный аспект обработки исключений.
    После обработки исключение удаляется из системы. Таким образом, в этой программе

    Глава 13. Обработка исключительных ситуаций
    357 при каждом проходе через цикл заново вводится try
    -блок, обеспечивая полную
    “готовность” к обработке следующих исключений. Такая организация позволяет обрабатывать в программах повторяющиеся ошибки.
    Использование нескольких catch-инструкций
    С try
    -блоком можно связать не одно, а несколько catch
    -инструкций. И это — довольно распространенная практика программирования. Однако все catch
    -инструкции должны перехватывать исключения различного типа. Например, следующая программа перехватывает как ошибку нарушения границ массива, так и ошибку деления на нуль.
    // Использование нескольких catch-инструкций. using System; class ExcDemo4 { public static void Main() {
    //
    Здесь массив numer длиннее массива denom. int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 }; int[] denom = { 2, 0, 4, 4, 0, 8 }; for(int i=0; i < numer.Length; i++) { try
    {
    Console.WriteLine(numer[i] + " / " + denom[i]
    +
    " равно " + numer[i]/denom[i]);
    } catch(DivideByZeroException)
    {
    // Перехватываем исключение.
    Console.WriteLine("Делить на нуль нельзя!");
    } catch(IndexOutOfRangeException)
    {
    // Перехватываем исключение.
    Console.WriteLine("Нет соответствующего элемента.");
    }
    }
    }
    }
    Эта программа генерирует следующие результаты:
    4/2 равно 2
    Делить на нуль нельзя!
    16/4 равно 4 32/4 равно 8
    Делить на нуль нельзя!
    128 / 8 равно 16
    Нет соответствующего элемента.
    Нет соответствующего элемента.
    Как подтверждают результаты выполнения этой программы, каждая catch-инструкция реагирует только на собственный тип исключения.
    В общем случае catch-выражения проверяются в том порядке, в котором они встречаются в программе. Выполняется только инструкция, тип исключения которой совпадает со сгенерированным исключением. Все остальные catch-блоки игнорируются.

    1   ...   18   19   20   21   22   23   24   25   ...   52


    написать администратору сайта