Г. Шилдт - Полный справочник по C# (1160789), страница 65
Текст из файла (страница 65)
Исключение, сгенерированное методом,вызванным из try-блока, может быть перехвачено этим try-блоком, если, конечно,етод сам не перехватит это исключение. Рассмотрим пример./* Исключение может сгенерировать один метод, аперехватить его — другой. */using System;class ExcTest {// Генерируем исключение,public s t a t i c void genException()i n t [ ] nums = new i n t [ 4 ] ;Console.WriteLine("Перед{генерированием исключения.");// Генерируем исключение, связанное с попаданием// индекса вне диапазона.f o r ( i n t 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 3 : Оnums [ 1 ] : 1nums [ 2 ] : 2nums[3]: 3Индекс вне диапазона!После catch-инструкции.Поскольку метод genExceptionO вызывается из блока t r y , исключение, котороеон генерирут (и не перехватывает), перехватывается инструкцией c a t c h в методеMain (). Но если бы метод genExceptionO перехватывал это исключение, оно быникогда не вернулось в метод Main ().Последствия возникновениянеперехватываемых исключенийПерехват одного из стандартных С#-исключений, как показала предыдущая программа, имеет побочный эффект: он предотвращает аварийное окончание программы.При генерировании исключения оно должно быть перехвачено программным кодом.Если программа не перехватывает исключение, оно перехвачивается С#-системой динамического управления.
Но дело в том, что система динамического управления сообщит об ошибке и завершит программу. Например, в следующем примере исключение, связанное с нарушением границ диапазона, программой не перехватывается.// Предоставим возможность обработать ошибку// С#-системе динамического управления.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]);При неверном индексировании массива выполнение программы останавливается,и на экране отображается следующее сообщение об ошибке:354Часть I.
Язык С#IUnhandled Exception: System.IndexOutOfRangeException:Index was outside the bounds of the array,at NotHandled.MainOЭто сообщение уведомляет об обнаружении в методе NotHandled.Main () необработанного исключения типа System. IndexOutOfRangeException, которое связано свыходом индекса массива за границы диапазона.Несмотря на то что такое сообщение может быть полезным во время отладкипрограммы, вряд ли вы захотите, чтобы его увидели пользователи! Поэтому важно,чтобы программы сами обрабатывали подобные исключения.Как упоминалось выше, тип исключения должен совпадать с типом, заданным вcatch-инструкции. В противном случае это исключение не будет перехвачено. Например, в следующей программе делается попытка перехватить ошибку нарушенияиндексом массива границ диапазона с помощью catch-инструкции для классаDivideByZeroException (это еще одно из встроенных С#-исключений).
При нарушении границ диапазона, допустимого для индекса массива, генерируется исключение типа 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 ] : 0nums[1]: 1nums[2]: 2nums[3 3: 3Глава 13. Обработка исключительных ситуаций355IUnhandled Exception: System.IndexOutOfRangeException:Index was outside the bounds of the array,at ExcTypeMismatch.Main()Как подтверждают результаты выполнения этой программы, catch-инструкция,предназначенная для перехвата исключения типа DivideByZeroException, не в состоянии перехватить исключение типа IndexOutOfRangeException.Возможность красиво выходить из ошибочныхситуацийОдно из основных достоинств обработки исключений состоит в том, что она позволяет программе отреагировать на ошибку и продолжить выполнение.
Рассмотрим,например, следующую программу, которая делит элементы одного массива на элементы другого. Если при этом встречается деление на нуль, генерируется исключение типа DivideByZeroException. В программе это исключение обрабатывается выдачейсообщения об ошибке, после чего выполнение программы продолжается. Следовательно, попытка разделить на нуль не вызывает внезапную динамическую ошибку, врезультате которой прекращается выполнение программы. Вместо аварийного останова исключение позволяет красиво выйти из ошибочной ситуации и продолжить выполнение программы.// Достойная реакция на ошибку и продолжение работы —// вот что значит с толком использовать исключения!using System;c l a s s ExcDemo3 {p u b l i c s t a t i c void Main() {i n t [ ] numer = { 4, 8, 16, 32, 64, 128 };i n t [ ] denom = { 2, 0, 4, 4, 0, 8 };for(int i=0; i < numer.Length;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Эта программа демонстрирует еще один важный аспект обработки исключений.После обработки исключение удаляется из системы.
Таким образом, в этой программе356Часть I. Язык С#при каждом проходе через цикл заново вводится try-блок, обеспечивая полную"готовность" к обработке следующих исключений. Такая организация позволяет обрабатывать в программах повторяющиеся ошибки.Использование нескольких catch-инструкцийС try-блоком можно связать не одно, а несколько catch-инструкций. И это —довольно распространенная практика программирования.
Однако все c a t c h инструкции должны перехватывать исключения различного типа. Например, следующая программа перехватывает как ошибку нарушения границ массива, так и ошибкуделения на нуль.// Использование нескольких catch-инструкций.u s i n g System;c l a s s ExcDemo4 {p u b l i c s t a t i c void Main() {// Здесь массив numer длинне массива denom.i n t [ ] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };i n t [ ] denom = { 2 , 0 , 4 , 4 ,0,8};for(int i=0; i < numer.Length;try {Console.WriteLine(numer[i] + " / " +denom[i] + " равно " +numer[i]/denom[i]);}catch (DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("Делить на нуль нельзя!");}catch (IndexOutOfRangeException) {// Перехватываем исключение.Console.WriteLine("Нет соответствующего элемента.")Эта программа генерирурет следующие результаты:4/2равно 2Делить на нуль нельзя!16/4равно 432/4равно 8Делить на нуль нельзя!128 / 8 равно 16Нет соответствующего элемента.Нет соответствующего элемента.Как подтверждают результаты выполнения этой программы, каждая catchинструкция реагирует только на собственный тип исключения.В общем случае catch-выражения проверяются в том порядке, в котором онивстречаются в программе.
Выполняется только инструкция, тип исключения которойсовпадает со сгенерированным исключением. Все остальные catch-блоки игнорируются.Глава 13. Обработка исключительных ситуаций357Перехват всех исключенийИногда требуется перехватывать все исключения, независимо от их типа. Для этогоиспользуйте catch-инструкцию без параметров. В этом случае создается обработчик"глобального перехвата", который используется, чтобы программа гарантированнообработала все исключения. В следующей программе приведен пример использованиятакого обработчика, который успешно перехватывает генерируемые здесь исключениетипа IndexOutOfRangeException и исключение типа DivideByZeroException.// Использование catch-инструкции для// "глобального перехвата".using System;class ExcDemo5 {public static void Main() {// Здесь массив numer длиннее массива denoiti.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;try {Console.WriteLine(numer[i] + " / " +denom[i] + " равно " +numer[i]/denom[i]);}catch {Console.WriteLine("Произошло некоторое исключение.");Вот как выглядят результаты выполнения этой программы:4 / 2 равно 2Произошло некоторое исключение.1 6 / 4 равно 43 2 / 4 равно 8Произошло некоторое исключение.128 / 8 равно 16Произошло некоторое исключение.Произошло некоторое исключение.В отношении catch-инструкции, предназначенной для "глобального перехвата",необходимо запомнить следующее: она должна быть последней в последовательностиcatch-инструкций.Вложение try-блоковОдин try-блок можно вложить в другой.
Исключение, сгенерированное во внутреннем try-блоке и не перехваченное catch-инструкцией, которая связана с этимtry-блоком, передается во внешний try-блок. Например, в следующей программеисключение типа IndexOutOfRangeException перехватывается не внутренним t r y блоком, а внешним.358Часть I. Язык С#// Использование вложенного try-блока,using System;class NestTrys {public static void Main() {// Здесь массив numer длиннее массива denom.int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };int[] denom = { 2, 0, 4, 4, 0, 8 };try { // Внешний try-блок.for(int i=0; i < numer.Length; i++) {try { // Вложенный try-блок.Console.WriteLine(numer[i] + " / " +denom[i] + " равно " +numer[i]/denom[i]);}catch (DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("На нуль делить нельзя!");catch (IndexOutOfRangeException) {// Перехватываем исключение.Console.WriteLine("Нет соответствующего элемента.");Console.WriteLine("Неисправимая ошибка — программа завершена.");Вот результаты выполнения этой программы:4 / 2 равно 2На нуль делить нельзя!1 6 / 4 равно 43 2 / 4 равно 8На нуль делить нельзя!128 / 8 равно 16Нет соответствующего элемента.Неисправимая ошибка — программа завершена.Исключение, которое может быть обработано внутренним try-блоком (в данномслучае это деление на нуль), позволяет программе продолжать работу.