Г. Шилдт - Полный справочник по C# (1160789), страница 66
Текст из файла (страница 66)
Однако нарушение границ массива перехватывается внешним try-блоком и заставляет программузавершиться.В предыдущей программе хочется обратить ваше внимание вот на что. Чаще всегоиспользование вложенных try-блоков обусловлено желанием обрабатывать различные категории ошибок различными способами.
Одни типы ошибок носят катострофический характер и не подлежат исправлейию. Другие — неопасны для дальнейшегофункционирования программы, и с ними можно справиться прямо на месте их возникновения. Многие программисты используют внешний try-блок для перехвата самых серьезных ошибок, позволяя внутренним try-блокам обрабатывать менее опасные. Внешние try-блоки можно также использовать в качестве механизма "глобального перехвата" для обработки тех ошибок, которые не перехватываются внутреннимблоком.Глава 13.
Обработка исключительных ситуаций359Генерирование исключений вручнуюВ предыдущих примерах демонстрировался перехват исключений, сгенерированных автоматически средствами С#. Однако можно сгенерировать исключение вручную, используя инструкцию throw. Формат ее записан таков:throw exceptOb;Элемент exceptOb — это объект класса исключений, производного от классаException.Рассмотрим пример, который демонстрирует использование инструкции throw для^нерирования исключения типа DivideByZeroException вручную.// Генерирование исключения вручную.using System;class ThrowDemo {public static void Main() {try {Console.WriteLine("До генерирования исключения.");throw new DivideByZeroException();}catch (DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("Исключение перехвачено.");}Console.WriteLine("После try/catch-блока.");|Результаты выполнения этой программы имеют такой вид:До генерирования исключения.Исключение перехвачено.После try/catch-блока.Обратите внимание на то, как был создан объект исключения типа DivideByZeroException, а именно: с помощью оператора new в инструкции throw.
Помните, что инструкция throw генерирует объект, ведь нельзя просто сгенерировать "типисключения". В данном случае при создании объекта класса DivideByZeroException использовался конструктор по умолчанию, но для генерирования исключений предусмотрены и другие конструкторы.Чаще всего генерируемые исключения являются экземплярами классов исключений, создаваемых в программе. Как будет показано далее в этой главе, создание собственных классов исключений позволяет обрабатывать ошибки в коде, и эта процедура может стать частью общей стратегии обработки исключений программы.Повторное генерирование исключенийИсключение, перехваченное одной catch-инструкцией, можно перегенерировать,чтобы обеспечить возможность его перехвата другой (внешней) catch-инструкцией.Самая распространенная причина для повторного генерирования исключения — позволить нескольким обработчикам получить доступ к исключению.
Например, возможна такая ситуация, что один обработчик исключений управляет одним аспектомисключения, а второй — другим. Чтобы повторно сгенерировать исключение, доста-360Часть I. Язык С#точно использовать ключевое слово throw, не указывая исключения. Другими словами, используйте следующую форму инструкции throw,throw ;Помните, что при повторном генерировании исключения оно не будет повторноперехватываться той же catch-инструкцией, а передается следующей c a t c h инструкции.Повторное генерирование исключений демонстрируется в следующей программе.B данном случае она перегенерирует исключение типа IndexOutOfRangeException.// Повторное генерирование исключения.usingSystem;c l a s s Rethrow {p u b l i c s t a t i c void genException() {// Здесь массив numer длиннее массива denom.i n t [ ] numer = { 4, 8, 16, 32, 64, 128, 256, 512i n t [ ] denom = { 2 , 0 , 4, 4,0,8};};for(int i=0; i<numer.Length;try {Console.WriteLine(numer[i] + " / " +denom[i] + " равно " +numer[l]/denom[i]);}catch (DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("Делить на нуль нельзя!")}catch (IndexOutOfRangeException) {// Перехватываем исключение.Console.WriteLine("Нет соответствующего элемента.")throw; // Генерируем исключение повторно.class RethrowDemo {public static void Main() {try {Rethrow.genException();}catch(IndexOutOfRangeException) {// Перехватываем повторно// сгенерированное исключение.Console.WriteLine("Неисправимая ошибка —"программа завершена.");В этой программе ошибки деления на нуль обрабатываются локально (по месту),т.е.
в самом методе genException (), но ошибка нарушения границ массива генерируется повторно. В данном случае исключение типа IndexOutOfRangeException обрабатывается функцией Main ().Глава 13. Обработка исключительных ситуаций.361Использование блока f i n a l l yИногда возникает потребность определить программный блок, который долженвыполняться по выходу из try/catch-блока. Например, исключение может вызватьошибку, которая завершает текущий метод и, следовательно, является причинойпреждевременного возврата. Однако такой метод может оставить открытым файл илисоединение с сетью, которые необходимо закрыть.
Подобные обстоятельства — обычное явление в программировании, и С# предоставляет удобный путь выхода из них:блок finally.Чтобы определить блок кода, подлежащий выполнению по выходу из try/catchблока, включите в конец try/catch-последовательности блок f i n a l l y . Общая формазаписи последовательности try/catch-блоков, содержащей блок f i n a l l y , выглядитследующим образом.try {// Блок кода, предназначенный для обработки ошибок.}catch {ExcepTypelII Обработчик для)catch {ЕхсерТуре2II Обработчик дляexOb) {исключения типаExcepTypel.ехОЬ) {исключения типа ЕхсерТуре2.finally {// Код завершения обработки исключений.}Блок f i n a l l y будет выполнен после выхода из try/catch-блока, независимо отусловий его выполнения. Другими словами, при нормальном завершении try-блокаили в условиях возникновения исключения содержимое finally-блока будет безусловно отработано.
Блок finally выполнится и в том случае, если любой код внутриtry-блока или любая из его catch-инструкций определены внутри метода.Вот пример использования блока finally:// Использование блока finally.using System;class UseFmally {public s t a t i c void genException(int what) {int t ;i n t [ ] nums = new i n t [ 2 ] ;Console.WriteLine("Получаем " + what);try {switch(what) {case 0:t = 10 / what; // Генерируем ошибку// деления на нуль,break;case 1:nums[4] = 4 ; // Генерируем ошибку// индексирования массива.362.Часть I. Язык С#break;case 2:return; // Возврат из try-блока.catch (DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("На нуль делить нельзя!");return; // Возврат из catch-блока.}catch (IndexOutOfRangeException) {// Перехватываем исключение.Console.WriteLine("Нет соответствующего элемента.");}finally {Console.WriteLine("По окончании try-блока.");class FinallyDemo {public static void MainO {for(int i=0; i < 3;UseFinally.genException(i);Console.WriteLine();Вот какие результаты получены при выполнении этой программы:Получаем ОНа нуль делить нельзя!По окончании try-блока.Получаем 1Нет соответствующего элемента.По окончании try-блока.Получаем 2По окончании t r y - б л о к а .Как подтверждают результаты выполнения этой программы, независимо от итогазавершения try-блока, блок f i n a l l y выполняется обязательно.Исключения "под микроскопом"До сих пор мы перехватывали исключения, но ничего не делали с самим объектомисключения.
Как упоминалось выше, catch-инструкция позволяет задать тип исключения и параметр. Параметр предназначен для принятия объекта исключения. Поскольку все классы исключений являются производными от класса Exception, всеисключения поддерживают члены, определенные в этом классе. В этой главе мы рассмотрим наиболее полезные члены и конструкторы класса Exception и узнаем преимущества использования параметра catch-инструкции.В классе Exception определен ряд свойств.
Самые интересные из них: Message,StackTrace и T a r g e t S i t e . Все они предназначены только для чтения. СвойствоГлава 13. Обработка исключительных ситуаций363Message содержит строку, которая описывает причину ошибки, а свойствоStackTrace — строку со стеком вызовов, приведших к возникновению исключений.Свойство T a r g e t S i t e принимает объект, который задает метод, сгенерировавший исключение.В классе Exception также определен ряд методов. Чаще всего используется методToStringO, который возвращает строку с описанием исключения. МетодToStringO автоматически вызывается, если некоторое исключение отображается,например, с помощью метода WriteLine ( ) .
Использование свойств и методов классаException демонстрируется в следующей программе.// Использование членов класса Exception.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 UseExcept {public static void Main() {try {ExcTest.genException();}c a t c h (IndexOutOfRangeException exc) {// Перехватываем исключение.Console.WriteLine("Стандартное сообщение таково: " ) ;C o n s o l e . W r i t e L i n e ( e x c ) ; // Вызов метода T o S t r i n g O .Console.WriteLine("Свойство StackTrace: " +exc.StackTrace);Console.WriteLine("Свойство Message: " +exc.Message);Console.WriteLine("Свойство T a r g e t S i t e : " +exc.TargetSite);}Console.WriteLine("После catch-инструкции.");Вот результаты выполнения этой программы:Перед генерированием исключения.nums[0]: 0nums[1]: 1nums[2]: 2nums[3]: 3364/JгЧасть I.