Выбрать главу

Один блок try может быть вложен в другой. Исключение, генерируемое во вну треннем блоке try и не перехваченное в соответствующем блоке catch, передается во внешний блок try. В качестве примера ниже приведена программа, в которой исклю чение IndexOutOfRangeException перехватывается не во внутреннем, а во внешнем блоке try. // Использовать вложенный блок try. using System; class NestTrys { 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 Делить на нуль нельзя! 16/4 равно 4 32/4 равно 8 Делить на нуль нельзя! 128 / 8 равно 16 Подходящий элемент не найден. Неисправимая ошибка - программа прервана.

В данном примере исключение, обрабатываемое во внутреннем блоке try и связан ное с ошибкой из-за деления на нуль, не мешает дальнейшему выполнению програм мы. Но ошибка нарушения границ массива, обнаруживаемая во внешнем блоке try, приводит к прерыванию программы.

Безусловно, приведенный выше пример демонстрирует далеко не единственное основание для применения вложенных блоков try, тем не менее из него можно сделать важный общий вывод. Вложенные блоки try нередко применяются для обработки раз личных категорий ошибок разными способами. В частности, одни ошибки считаются неисправимыми и не подлежат исправлению, а другие ошибки незначительны и могут быть обработаны немедленно. Как правило, внешний блок try служит для обнаруже ниям обработки самых серьезных ошибок, а во внутренних блоках try обрабатываются менее серьезные ошибки. Кроме того, внешний блок try может стать "универсальным" для тех ошибок, которые не подлежат обработке во внутреннем блоке. Генерирование исключений вручную

В приведенных выше примерах перехватывались исключения, генерировавшиеся исполняющей системой автоматически. Но исключение может быть сгенерировано и вручную с помощью оператора throw. Ниже приведена общая форма такого гене рирования: throw exceptOb;

где в качестве exceptOb должен быть обозначен объект класса исключений, произво дного от класса Exception.

Ниже приведен пример программы, в которой демонстрируется применение опе ратора throw для генерирования исключения DivideByZeroException. // Сгенерировать исключение вручную. using System; class ThrowDemo { static void Main() { try { Console.WriteLine("До генерирования исключения."); throw new DivideByZeroException(); } catch (DivideByZeroException) { Console.WriteLine("Исключение перехвачено."); } Console.WriteLine("После пары операторов try/catch."); } }

Вот к какому результату приводит выполнение этой программы. До генерирования исключения. Исключение перехвачено. После пары операторов try/catch.

Обратите внимание на то, что исключение DivideByZeroException было сге нерировано с использованием ключевого слова new в операторе throw. Не следует забывать, что в данном случае генерируется конкретный объект, а следовательно, он должен быть создан перед генерированием исключения. Это означает, что сгенериро вать исключение только по его типу нельзя. В данном примере для создания объекта DivideByZeroException был автоматически вызван конструктор, используемый по умолчанию, хотя для генерирования исключений доступны и другие конструкторы. Повторное генерирование исключений

Исключение, перехваченное в одном блоке catch, может быть повторно сгенери ровано в другом блоке, чтобы быть перехваченным во внешнем блоке catch. Наиболее вероятной причиной для повторного генерирования исключения служит предоставле ние доступа к исключению нескольким обработчикам. Допустим, что один обработчик оперирует каким-нибудь одним аспектом исключения, а другой обработчик — другим его аспектом. Для повторного генерирования исключения достаточно указать опера тор throw без сопутствующего выражения, как в приведенной ниже форме. throw ;

Не следует, однако, забывать, что когда исключение генерируется повторно, то оно не перехватывается снова тем же самым блоком catch, а передается во внешний блок catch.

В приведенном ниже примере программы демонстрируется повтор ное генерирование исключения. В данном случае генерируется исключение IndexOutOfRangeException. // Сгенерировать исключение повторно. using System; class Rethrow { public static void GenException() { // Здесь массив 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("Подходящий элемент не найден."); throw; // сгенерировать исключение повторно } } } } class RethrowDemo { static void Main() { try { Rethrow.GenException(); } catch(IndexOutOfRangeException) { // перехватить исключение повторно Console.WriteLine("Неисправимая ошибка - программа прервана."); } } }

В этом примере программы ошибки из-за деления на нуль обрабатываются локаль но в методе GenException(), но ошибка выхода за границы массива генерируется повторно. В данном случае исключение IndexOutOfRangeException обрабатывается в методе Main(). Использование блока finally

Иногда требуется определить кодовый блок, который будет выполняться после вы хода из блока try/catch. В частности, исключительная ситуация может возникнуть в связи с ошибкой, приводящей к преждевременному возврату из текущего метода. Но в этом методе мог быть открыт файл, который нужно закрыть, или же установлено сетевое соединение, требующее разрывания. Подобные ситуации нередки в програм мировании, и поэтому для их разрешения в C# предусмотрен удобный способ: вос пользоваться блоком finally.

Для того чтобы указать кодовый блок, который должен выполняться после блока try/catch, достаточно вставить блок finally в конце последовательности операторов try/catch. Ниже приведена общая форма совместного использования блоков try/ catch и finally. try { // Блок кода, предназначенный для обработки ошибок. } catch (ExcepType1 exOb) { // Обработчик исключения типа ExcepType1. } catch (ExcepType2 ехОb) { // Обработчик исключения типа ЕхсерТуре2. } finally { // Код завершения обработки исключений. }

Блок finally будет выполняться всякий раз, когда происходит выход из блока try/ catch, независимо от причин, которые к этому привели. Это означает, что если блок try завершается нормально или по причине исключения, то последним выполняется код, определяемый в блоке finally. Блок finally выполняется и в том случае, если любой код в блоке try или в связанных с ним блоках catch приводит к возврату из метода.

Ниже приведен пример применения блока finally. // Использовать блок finally. using System; class UseFinally { public static void GenException(int what) { int t; int[] nums = new int[2]; Console.WriteLine("Получить " + what); try { switch(what) { case 0: t = 10 / what; // сгенерировать ошибку из-за деления на нуль break; case 1: nums[4] = 4; // сгенерировать ошибку индексирования массива break; case 2: return; // возврат из блока try } } catch (DivideByZeroException) { Console.WriteLine("Делить на нуль нельзя!"); return; // возврат из блока catch } catch (IndexOutOfRangeException) { Console.WriteLine("Совпадающий элемент не найден."); } finally { Console.WriteLine("После выхода из блока try."); } } } class FinallyDemo { static void Main() { for(int i=0; i < 3; i++) { UseFinally.GenException(i); Console.WriteLine(); } } }

Вот к какому результату приводит выполнение этой программы. Получить 0 Делить на нуль нельзя После выхода из блока try. Получить 1 Совпадающий элемент не найден. После выхода из блока try. Получить 2 После выхода из блока try.

Как следует из приведенного выше результата, блок finally выполняется независи мо от причины выхода из блока try.

И еще одно замечание: с точки зрения синтаксиса блок finally следует после блока try, и формально блоки catch для этого не требуются. Следовательно, блок finally можно ввести непосредственно после блока try, опустив блоки catch. В этом случае блок finally начнет выполняться сразу же после выхода из блока try, но исключения обрабатываться не будут. Подробное рассмотрение класса Exception

В приведенных выше примерах исключения только перехватывались, но никакой существенной обработке они не подвергались. Как пояснялось выше, в операторе catch допускается указывать тип и переменную исключения. Переменная получает ссылку на объект исключения. Во всех исключениях поддерживаются члены, опреде ленные в классе Exception, поскольку все исключения являются производными от этого класса. В этом разделе будет рассмотрен ряд наиболее полезных членов и кон структоров класса Exception и приведены конкретные примеры использования пере менной исключения.

В классе Exception определяется ряд свойств. К числу самых интересных отно сятся три свойства: Message, StackTrace и TargetSite. Все эти свойства доступны только для чтения. Свойство Message содержит символьную строку, описывающую характер ошибки; свойство StackTrace — строку с вызовами стека, приведшими к ис ключительной ситуации, а свойство TargetSite получает объект, обозначающий ме тод, сгенерировавший исключение.