На этом шаге мы рассмотрим особенности такой обработки исключений.
В примере предыдущего шага в процессе выполнения программы генерировались исключения разных классов, но перехватывались и обрабатывались они в одном блоке. Другими словами, для обработки ошибок разных типов мы использовали один и тот же блок программного кода. Правда мы использовали объект исключения, что позволяло нам определить фактический класс сгенерированного исключения, но принципиально это мало что меняет: какая бы ошибка ни возникла, для обработки выполнялся один и тот же код. Но процесс перехвата и обработки исключений можно организовать более гибким способом. Мы можем для каждого типа ошибки предусмотреть персональный блок обработки. Делается это просто. После try-блока указывается не один, а несколько catch-блоков. В каждом catch-блоке указывается (в круглых скобках) тип ошибки, который обрабатывается этим блоком. При возникновении ошибки в try-блоке выполнение команд контролируемого кода прекращается и начинается просмотр catch-блоков на предмет того, совпадает ли класс сгенерированного исключения с классом ошибки, указанным в catch-блоке. Если совпадение найдено, соответствующий catch-блок используется для обработки исключения. Если все catch-блоки просмотрены и совпадение не найдено, ошибка остается необработанной.
В описании catch-блока тип исключения указывается в круглых скобках после ключевого слова catch. При необходимости там же можно обозначить объект исключения и использовать его для получения информации о возникшем событии. В примере ниже представлена программа (которая является вариацией примера из предыдущего шага). В ней используется несколько catch-блоков для обработки исключений. Поскольку многие команды в программе должны быть вам уже знакомы, то для сокращения объема программного кода комментарии удалены.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr223_1 { class Program { static void Main() { Random rnd = new Random(); int[] nums; int x; for (int k = 1; k < 10; k++) { Console.Write("[{0}] ", k); try { nums = new int[2 * rnd.Next(3) - 1]; x = 1 / rnd.Next(3); nums[rnd.Next(2) - 1] = x; nums[0] = Int32.Parse("ноль"); } catch (OverflowException) { Console.WriteLine("Ошибка №1: Неверный размер массива"); } catch (DivideByZeroException) { Console.WriteLine("Ошибка №2: Деление на ноль"); } catch (IndexOutOfRangeException) { Console.WriteLine("Ошибка №3: Неверный индекс элемента"); } catch (FormatException) { Console.WriteLine("Ошибка №4: Неверный формат числа"); } } // Задержка: Console.ReadLine(); } } }
С поправкой на использование генератора случайных чисел результат выполнения программы может быть следующим:
Рис.1. Результат выполнения программы
В данном случае мы отказались от переменной n, с помощью которой ранее запоминалась команда, вызывавшая ошибку. Принцип выполнения программного кода такой же, как и в предыдущем случае. Отличие лишь в том, что при возникновении ошибки (причины возникновения ошибок не изменились) обработка ошибки выполняется одним из четырех catch-блоков. Класс ошибки, обрабатываемой catch-блоком, указан в круглых скобках после ключевого слова catch. Объект ошибки при этом мы не объявляли (хотя могли бы), поскольку в процессе обработки ошибок он не используется. В зависимости от того, какого типа ошибка произошла, для обработки ошибки выполняется код соответствующего catch-блока.
На следующем шаге мы рассмотрим вложенные конструкции try-catch и блок finally.