Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Следует особо подчеркнуть, что весь код, выполняемый в блоке try, контролируется на предмет исключительных ситуаций, в том числе и тех, которые могут возникнутьв результате вызова метода из самого блока try. Исключение, генерируемое методомв блоке try, может быть перехвачено в том же блоке, если, конечно, этого не будетсделано в самом методе.
В качестве еще одного примера рассмотрим следующую программу, где блок tryпомещается в методе Main(). Из этого блока вызывается метод GenException(), в котором и генерируется исключение IndexOutOfRangeException. Это исключение неперехватывается методом GenException(). Но поскольку метод GenException() вызывается из блока try в методе Main(), то исключение перехватывается в блоке catch,связанном непосредственно с этим блоком 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("He подлежит выводу"); }}class ExcDemo2 { static void Main() { try { ExcTest.GenException(); } catch (IndexOutOfRangeException) { // Перехватить исключение. Console.WriteLine("Индекс вышел за границы массива!"); } Console.WriteLine("После блока перехвата исключения."); }}
Выполнение этой программы дает такой же результат, как и в предыдущемпримере.До генерирования исключения.nums[0]: 0nums[1]: 1nums[2]: 2nums[3]: 3Индекс вышел за границы массива!После блока перехвата исключения.
Как пояснялось выше, метод GenException() вызывается из блока try, и поэтомугенерируемое им исключение перехватывается не в нем, а в блоке catch внутри метода Main(). А если бы исключение перехватывалось в методе GenException(), оно небыло бы вообще передано обратно методу Main().Последствия неперехвата исключений
Перехват одного из стандартных исключений, как в приведенных выше примерах,дает еще одно преимущество: он исключает аварийное завершение программы. Кактолько исключение будет сгенерировано, оно должно быть перехвачено каким-то фрагментом кода в определенном месте программы. Вообще говоря, если исключение неперехватывается в программе, то оно будет перехвачено исполняющей системой. Нодело в том, что исполняющая система выдаст сообщение об ошибке и прервет выполнение программы. Так, в приведенном ниже примере программы исключение в связис выходом индекса за границы массива не перехватывается.// Предоставить исполняющей системе C# возможность самой обрабатывать ошибки.using System;class NotHandled { 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]); } }}
Когда возникает ошибка индексирования массива, выполнение программы прерывается и выдается следующее сообщение об ошибке.Необработанное исключение: System.IndexOutOfRangeException: Индекс находился вне границ массива. в NotHandled.Main() в <имя_файла>:строка 16
Это сообщение уведомляет об обнаружении в методе NotHandled.Main() необработанного исключения типа System.IndexOutOfRangeException, которое связанос выходом индекса за границы массива.
Такие сообщения об ошибках полезны для отладки программы, но, по меньшемере, нежелательны при ее использовании на практике! Именно поэтому так важноорганизовать обработку исключительных ситуаций в самой программе.
Как упоминалось ранее, тип генерируемого исключения должен соответствоватьтипу, указанному в операторе catch. В противном случае исключение не будет перехвачено. Например, в приведенной ниже программе предпринимается попытка перехватить ошибку нарушения границ массива в блоке catch, реагирующем на исключение DivideByZeroException, связанное с делением на нуль и являющееся еще однимстандартным исключением. Когда индексирование массива выходит за его границы,генерируется исключение IndexOutOfRangeException, но оно не будет перехваченоблоком catch, что приведет к аварийному завершению программы.// Не сработает!using System;class ExcTypeMismatch { 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("He подлежит выводу"); } /* Если перехват рассчитан на исключение DivideByZeroException, то перехватить ошибку нарушения границ массива не удастся. */ catch (DivideByZeroException) { // Перехватить исключение. Console.WriteLine("Индекс вышел за границы массива!"); } Console.WriteLine("После блока перехвата исключения."); }}
Вот к какому результату приводит выполнение этой программы.До генерирования исключения.nums[0]: 0nums[1]: 1nums[2]: 2nums[3]: 3Необработанное исключение: System.IndexOutOfRangeException: Индекс находился вне границ массива в ExcTypeMismatch.Main() в <имя_файла>:строка 18
Как следует из приведенного выше результата, в блоке catch, реагирующемна исключение DivideByZeroException, не удалось перехватить исключениеIndexOutOfRangeException.Обработка исключительных ситуаций - “изящный” способ устранения программных ошибок
Одно из главных преимуществ обработки исключительных ситуаций заключаетсяв том, что она позволяет вовремя отреагировать на ошибку в программе и затем продолжить ее выполнение. В качестве примера рассмотрим еще одну программу, в которой элементы одного массива делятся на элементы другого. Если при этом происходитделение на нуль, то генерируется исключение DivideByZeroException. Обработкаподобной исключительной ситуации заключается в том, что программа уведомляетоб ошибке и затем продолжает свое выполнение. Таким образом, попытка деленияна нуль не приведет к аварийному завершению программы из-за ошибки при ее выполнении. Вместо этого ошибка обрабатывается "изящно", не прерывая выполнениепрограммы.// Изящно обработать исключительную ситуацию и продолжить выполнение программы.using System;class ExcDemo3 { 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 равно 432/4 равно 8Делить на нуль нельзя!128 / 8 равно 16
Из данного примера следует еще один важный вывод: как только исключение обработано, оно удаляется из системы. Поэтому в приведенной выше программе проверкаошибок в блоке try начинается снова на каждом шаге цикла for, при условии, что всепредыдущие исключительные ситуации были обработаны. Это позволяет обрабатывать в программе повторяющиеся ошибки.Применение нескольких операторов catch
С одним оператором try можно связать несколько операторов catch. И на практике это делается довольно часто. Но все операторы catch должны перехватывать исключения разного типа. В качестве примера ниже приведена программа, в которойперехватываются ошибки выхода за границы массива и деления на нуль.// Использовать несколько операторов catch.using System;class ExcDemo4 { 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("Подходящий элемент не найден."); } } }}