1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 64
Текст из файла (страница 64)
Они просто определяют вершины двух различных иерархий классовисключений.C# определяет встроенные исключения, которые выводятся из классаSystemException. Например, при попытке выполнить деление на нуль генерируетсяисключение класса DivideByZeroException. Как будет показано ниже в этой главе, высможете создавать собственные классы исключений, выводя их из классаApplicationException.Основы обработки исключенийУправление C#-механизмом обработки исключений зиждется на четырех ключевыхсловах: try, catch, throw и finally. Они образуют взаимосвязанную подсистему,350Часть I. Язык C#в которой использование одного из них предполагает использование другого.
В этой главекаждое слово рассматривается подробно. Однако для начала будет полезно получить общеепредставление о роли, которую они играют в обработке исключительных ситуаций. Есликратко, то их работа состоит в следующем.Программные инструкции, которые нужно проконтролировать на предмет исключений,помещаются в 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#. В следующейпрограмме такое исключение намеренно генерируется, а затем перехватывается.Глава 13. Обработка исключительных ситуаций351// Демонстрация обработки исключений.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]: 0nums[1]: 1nums[2]: 2nums[3]: 3Индекс вне диапазона!После catch-инструкции.Обратите внимание на то, что nums — это int-массив для хранения четырехэлементов.
Однако в цикле for делается попытка индексировать этот массив от 0 до 9, икак только значение индекса устанавливается равным четырем, генерируется исключениетипа IndexOutOfRangeException.Несмотря на небольшой размер, предыдущая программа иллюстрирует ряд ключевыхаспектов обработки исключений. Во-первых, проверяемый код содержится внутри tryблока.
Во-вторых, при возникновении исключения (в данном случае из-за попытки внутриfor-цикла индексировать массив nums за границами его диапазона) выполнение tryблока прекращается, а само исключение перехватывается catch-инструкцией. Другимисловами, управление программой передается catch-инструкции, независимо от того, всели инструкции try-блока выполнились. При этом важно то, что инструкция catch невызывается, а ей передается управление программой. Поэтому инструкцияConsole.WriteLine("После catch-инструкции."};никогда не выполнится. После выполнения catch-инструкции программа продолжится соследующей инструкции.
Следовательно, чтобы ваша программа могла нормальнопродолжить свое выполнение, обработчик должен устранить проблему, которая сталапричиной возникновения исключительной ситуации.352Часть I. Язык C#Обратите внимание на то, что в инструкции 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) {Глава 13. Обработка исключительных ситуаций353}}}// Перехватываем исключение.Console.WriteLine("Индекс вне диапазона!");Console.WriteLine("После catch-инструкции.");Эта программа показывает результаты, которые не отличаются от результатоввыполнения предыдущей ее версии:Перед генерированием исключения.nums[0]: 0nums[1]: 1nums[2]: 2nums[3]: 3Индекс вне диапазона!После catch-инструкции.Поскольку метод genException() вызывается из блока try, исключение, котороеон генерирует (и не перехватывает), перехватывается инструкцией catch в методеMain().
Но если бы метод genException() перехватывал это исключение, оно быникогда не вернулось в метод Main().Последствия возникновения неперехватываемыхисключенийПерехват одного из стандартных C#-исключений, как показала предыдущаяпрограмма, имеет побочный эффект: он предотвращает аварийное окончание программы.При генерировании исключения оно должно быть перехвачено программным кодом. Еслипрограмма не перехватывает исключение, оно перехватывается C#-системой динамическогоуправления. Но дело в том, что система динамического управления сообщит об ошибке изавершит программу.