Полное руководство. С# 4.0
Шрифт:
Выполнение этой программы приводит к следующему результату. 4/2 равно 2 Делить на нуль нельзя! 16/4 равно 4 32/4 равно 8 Делить на нуль нельзя! 128 / 8 равно 16 Подходящий элемент не найден. Неисправимая ошибка - программа прервана.
В данном примере исключение, обрабатываемое во внутреннем блоке 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