1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 65
Текст из файла (страница 65)
Например, в следующем примере исключение, связанное снарушением границ диапазона, программой не перехватывается.// Предоставим возможность обработать ошибку// C#-системе динамического управления.using System;class NotHandled {public static void Main() {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]);}При неверном индексировании массива выполнение программы останавливается, ина экране отображается следующее сообщение об ошибке:354Часть I. Язык C#Unhandled Exception: System.IndexOutOfRangeException:Index was outside the bounds of the array.at NotHandled.Main()Это сообщение уведомляет об обнаружении в методе NotHandled.Main()необработанного исключения типа System.IndexOutOfRangeException, котороесвязано с выходом индекса массива за границы диапазона.Несмотря на то что такое сообщение может быть полезным во время отладкипрограммы, вряд ли вы захотите, чтобы его увидели пользователи! Поэтому важно, чтобыпрограммы сами обрабатывали подобные исключения.Как упоминалось выше, тип исключения должен совпадать с типом, заданным вcatch-инструкции.
В противном случае это исключение не будет перехвачено. Например,в следующей программе делается попытка перехватить ошибку нарушения индексоммассиваграницдиапазонаспомощьюcatch-инструкциидляклассаDivideByZeroException (это еще одно из встроенных C#-исключений). Принарушении границ диапазона, допустимого для индекса массива, генерируется исключениетипа IndexOutOfRangeException, которое не перехватывается предусмотренной впрограмме catch-инструкцией. В результате программа завершается аварийно.// Эта программа работать не будет!using System;class ExcTypeMismatch {public static void Main() {int[] nums = new int[4];try {Console.WriteLine("Перед генерированием исключения.");}}}// Генерируем исключение, связанное с попаданием// индекса вне диапазона.for(int i=0; i < 10; i++) {nums[i] = i;Console.WriteLine("nums[{0}]: {1}", i, nums[i]);}Console.WriteLine("Этот текст не отображается.");/* Если в catch-инструкции указан тип исключенияDivideByZeroException, то с ее помощью невозможноперехватить ошибку нарушения границ массива.
*/catch(DivideByZeroException) {// Перехватываем исключение.Console.WriteLine("Индекс вне границ диапазона!");}Console.WriteLine("После catch-инструкции.");Вот как выглядят результаты выполнения этой программы:Перед генерированием исключения.nums[0]: 0nums[1]: 1nums[2]: 2nums[3]: 3Глава 13. Обработка исключительных ситуаций355Unhandled Exception: System.IndexOutOfRangeException:Index was outside the bounds of the array.at ExcTypeMismatch.Main()Как подтверждают результаты выполнения этой программы, catch-инструкция,предназначенная для перехвата исключения типа DivideByZeroException, не всостоянии перехватить исключение типа IndexOutOfRangeException.Возможность красиво выходить из ошибочныхситуацийОдно из основных достоинств обработки исключений состоит в том, что онапозволяет программе отреагировать на ошибку и продолжить выполнение.
Рассмотрим,например, следующую программу, которая делит элементы одного массива на элементыдругого. Если при этом встречается деление на нуль, генерируется исключение типаDivideByZeroException. В программе это исключение обрабатывается выдачейсообщения об ошибке, после чего выполнение программы продолжается. Следовательно,попытка разделить на нуль не вызывает внезапную динамическую ошибку, в результатекоторой прекращается выполнение программы. Вместо аварийного останова исключениепозволяет красиво выйти из ошибочной ситуации и продолжить выполнение программы.// Достойная реакция на ошибку и продолжение работы -// вот что значит с толком использовать исключения!using System;class ExcDemo3 {public static void Main() {int[] numer = { 4, 8, 16, 32, 64, 128 };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("Делить на нуль нельзя!");При выполнении эта программа демонстрирует следующие результаты:4/2 равно 2Делить на нуль нельзя!16/4 равно 432/4 равно 8Делить на нуль нельзя!128 / 8 равно 16Эта программа демонстрирует еще один важный аспект обработки исключений.После обработки исключение удаляется из системы.
Таким образом, в этой программе356Часть I. Язык C#при каждом проходе через цикл заново вводится try-блок, обеспечивая полную“готовность” к обработке следующих исключений. Такая организация позволяетобрабатывать в программах повторяющиеся ошибки.Использование нескольких catch-инструкцийС try-блоком можно связать не одно, а несколько catch-инструкций. И это —довольно распространенная практика программирования.
Однако все catch-инструкциидолжны перехватывать исключения различного типа. Например, следующая программаперехватывает как ошибку нарушения границ массива, так и ошибку деления на нуль.// Использование нескольких catch-инструкций.using System;class ExcDemo4 {public static void Main() {// Здесь массив 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("Нет соответствующего элемента.");}Эта программа генерирует следующие результаты:4/2 равно 2Делить на нуль нельзя!16/4 равно 432/4 равно 8Делить на нуль нельзя!128 / 8 равно 16Нет соответствующего элемента.Нет соответствующего элемента.Как подтверждают результаты выполнения этой программы, каждая catch-инструкцияреагирует только на собственный тип исключения.В общем случае catch-выражения проверяются в том порядке, в котором онивстречаются в программе.
Выполняется только инструкция, тип исключения которойсовпадает со сгенерированным исключением. Все остальные catch-блоки игнорируются.Глава 13. Обработка исключительных ситуаций357Перехват всех исключенийИногда требуется перехватывать все исключения, независимо от их типа. Для этогоиспользуйте catch-инструкцию без параметров. В этом случае создается обработчик“глобального перехвата”, который используется, чтобы программа гарантированнообработала все исключения. В следующей программе приведен пример использованиятакого обработчика, который успешно перехватывает генерируемые здесь исключение типаIndexOutOfRangeException и исключение типа DivideByZeroException.// Использование catch-инструкции для// "глобального перехвата".using System;class ExcDemo5 {public static void Main() {// Здесь массив 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 {Console.WriteLine("Произошло некоторое исключение.");}}Вот как выглядят результаты выполнения этой программы:4/2 равно 2Произошло некоторое исключение.16/4 равно 432/4 равно 8Произошло некоторое исключение.128 / 8 равно 16Произошло некоторое исключение.Произошло некоторое исключение.В отношении catch-инструкции, предназначенной для “глобального перехвата”,необходимо запомнить следующее: она должна быть последней в последовательностиcatch-инструкций.Вложение try-блоковОдин try-блок можно вложить в другой.
Исключение, сгенерированное вовнутреннем try-блоке и не перехваченное catch-инструкцией, которая связана с этимtry-блоком, передается во внешний try-блок. Например, в следующей программеисключение типа IndexOutOfRangeException перехватывается не внутренним tryблоком, а внешним.358Часть I. Язык C#// Использование вложенного try-блока.using System;class NestTrys {public 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 равно 432/4 равно 8На нуль делить нельзя!128 / 8 равно 16Нет соответствующего элемента.Неисправимая ошибка — программа завершена.Исключение, которое может быть обработано внутренним try-блоком (в данномслучае это деление на нуль), позволяет программе продолжать работу.
Однако нарушениеграниц массива перехватывается внешним try-блоком и заставляет программузавершиться.В предыдущей программе хочется обратить ваше внимание вот на что. Чаще всегоиспользование вложенных try-блоков обусловлено желанием обрабатывать различныекатегории ошибок различными способами. Одни типы ошибок носят катастрофическийхарактер и не подлежат исправлению. Другие — неопасны для дальнейшегофункционирования программы, и с ними можно справиться прямо на месте ихвозникновения. Многие программисты используют внешний try-блок для перехвата самыхсерьезных ошибок, позволяя внутренним try-блокам обрабатывать менее опасные.Внешние try-блоки можно также использовать в качестве механизма “глобальногоперехвата” для обработки тех ошибок, которые не перехватываются внутренним блоком.Генерирование исключений вручнуюГлава 13.
Обработка исключительных ситуаций359В предыдущих примерах демонстрировался перехват исключений, сгенерированныхавтоматически средствами C#. Однако можно сгенерировать исключение вручную,используя инструкцию 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 генерирует объект, ведь нельзя просто сгенерировать “типисключения”.