На этом шаге мы рассмотрим особенности использования указанных конструкций.
Один try-блок (внешний) может содержать в себе try-catch конструкции (внутренние). Интерес представляет ситуация, когда ошибка происходит во внутреннем try-блоке. У этого внутреннего try-блока есть catch-блоки, предназначенные для обработки исключений. Возможны две ситуации:
В первом случае все происходит штатно: ошибка обрабатывается, и после выполнения соответствующего catch-блока начинают выполняться команды после внутренней try-catch конструкции. А вот во втором случае, когда ошибка не обрабатывается во внутренней try-catch конструкции, в игру вступает внешняя try-catch конструкция. А именно, исключение, сгенерированное во внутреннем try-блоке, передается для обработки во внешнюю try-catch конструкцию. Если исключение обрабатывается в каком-то из catch-блоков этой конструкции, то дальше выполняются команды после внешней try-catch конструкции. Если ошибка не обрабатывается и во внешнем try-блоке, то исключение передается дальше, в следующую внешнюю try-catch конструкцию - конечно, если такая имеется. В конце концов, ошибка или будет обработана, или закончатся внешние try-catch конструкции. В последнем случае выполнение программы завершается и появляется сообщение об ошибке.
Конструкцию try-catch, состоящую из try-блока и catch-блоков, можно дополнить еще и finally-блоком. Этот блок указывается после catch-блоков. То есть можно использовать конструкцию try-catch-finally такого вида (жирным шрифтом выделены ключевые элементы шаблона):
try { // Контролируемый код } catch(класс объект){ // Команды для обработки исключения } finally { // Обязательные для выполнения команды }
В этой конструкции catch-блоков может быть несколько. Команды из блока finally выполняются в любом случае - и если ошибка в try-блоке возникла, и если ошибка в try-блоке не возникла. Возможны три принципиальные ситуации:
Собственно, ради вот этого третьего сценария обычно и используют блок finally. Небольшой пример, в котором иллюстрируется использование вложенных try-catch конструкций и блока finally, представлен в примере ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr224_1 { // Класс с главным методом: class Program { // Статический метод: static void generator(string num) { // Отображение сообщения: Console.WriteLine("Метод с аргументом \"{0}\" начинает paботу", num); // Текстовая переменная: string msg = "Массив не создан!"; // Контролируемый код: try { // Преобразование текста в целое число: intm = Int32.Parse(num); // Создание массива: int[] array = new int[m]; // Новое значение текстовой переменной: msg = "Создан массив: "; // Цикл: for (int k = 0; k <= 4; k++) { // Элементу массива присваивается значение: array[k] = 12 / (4 - k); // К тексту добавляется значение элемента: msg += " " + array[k]; } } // Обработка ошибки выхода за пределы массива: catch (IndexOutOfRangeException) { // Отображение сообщения: Console.WriteLine("Ошибка в методе: выход за пределы массива"); } // Код для обязательного выполнения: finally { // Отображение значения текстовой переменной: Console.WriteLine(msg); } // Отображение сообщения: Console.WriteLine("Meтод завершил работу"); } // Главный метод: static void Main() { // Текстовый массив: string[] args = { "один", "-2", "5", "2" }; // Символьный массив из одного элемента: char[] symbs = new char[1]; // Цикл: for (int k = 0; k < args.Length; k++) { // Контролируемый код (внешний блок): try { // Отображение сообщения: Console.WriteLine("Выполняется цикл №{0}: ", k + 1); // Контролируемый код (внутренний блок): try { // Вызов статического метода: generator(args[k]); // Команда с ошибкой (неверный индекс): symbs[1] = 'A'; } // Обработка ошибки деления на ноль // (внутренний блок): catch (DivideByZeroException e) { // Отображение сообщения: Console.WriteLine("Первый внутренний catch-блок: {0}", e.GetType().Name); } // Обработка ошибки выхода за пределы массива // (внутренний блок): catch (IndexOutOfRangeException e) { // Отображение сообщения: Console.WriteLine("Второй внутренний catch-блок: {0}", e.GetType().Name); } // Обязательный для выполнения код: finally { // Элементу массива присваивается значение: symbs[0] = (char)('A' + k); // Отображение сообщения: Console.WriteLine("Символ: \'{0}\'", symbs[0]); } } // Обработка всех ошибок (внешний блок): catch (Exception e) { // Отображение сообщения: Console.WriteLine("Внешний catch-блок: {0}", e.GetType().Name); } // Отображение сообщения: Console.WriteLine("Цикл №{0} завершен...\n", k + 1); } // Задержка: Console.ReadLine(); } } }
Результат выполнения программы может быть таким:
Рис.1. Результат выполнения программы
В программе, в классе с главным методом описан статический метод generator(). Метод не возвращает результат и имеет текстовый аргумент (обозначен как num). Предполагается, что это текстовое представление целого числа. Поэтому командой
intm = Int32.Parse(num);
int[] array = new int[m]; ,
msg = "Создан массив: ";
msg += " " + array[k];
array[k] = 12 / (4 - k); .
При выполнении внутреннего try-блока в любом случае возникает ошибка. Это или ошибки класса FormatException, OverflowException или DivideByZeroException, выбрасываемые при вызове метода generator(), или ошибка класса IndexOutOfRangeException, возникающая при выполнении команды
symbs[1] = 'A'; .
В главном методе программы командой
string[] args = { "один", "-2", "5", "2" };
char[] symbs = new char[1];
Тело цикла состоит из внешней try-catch конструкции и команды, отображающей сообщение о завершении выполнения соответствующей итерации цикла. Во внешнем catch-блоке обрабатываются исключения класса Exception (то есть фактически все ошибки). При обработке исключения отображается название класса ошибки.
Внешний try-блок состоит из команды, которой отображается сообщение о начале выполнения цикла, а также внутренней конструкции try-catch-finally. Внутренний try-блок состоит из команд
generator(args[k]);
symbs[1] = 'A'; .
symbs[1] = 'A'; .
generator(args[k]);
symbs[1] = 'A'; .
Для внутреннего try-блока предусмотрены catch-блоки, обрабатывающие ошибки классов DivideByZeroException и IndexOutOfRangeException. Есть также блок finally, при выполнении которого командой
symbs[0] = (char)('A' + k);
Чтобы понять результат выполнения программы, необходимо учесть следующие обстоятельства.
symbs[1] = 'A';
На следующем шаге мы рассмотрим генерацию исключений.