* При перепечатке материалов ссылка на www.SeoLiga.ru обязательна!
Обработка и генерация исключений
8 февраля 2009
Даже тестирование и отладка приложения не исключают возможность возникнове- ния ошибок в период выполнения. Рассмотрим для примера приложение, записы- вающее данные на гибкий диск. Если оно попытается сохранить данные, когда диск не готов, непременно произойдет ошибка, даже если код приложения сам по себе не содержит ошибок и работает, как задумано. Языки Visual Basic .NET и Visual C# поддерживают структурную обработку исключений, которая позволяет создавать код, способный корректно обрабатывать ошибки, и значит, продолжить выполне- ние программы. Кроме того, можно создавать объекты исключений и генерировать исключения внутри компонентов для уведомления основной программы о возник- новении ошибки.
Изучив материал этого занятия, вы сможете: ^ описать обработку исключений общеязыковой исполняющей средой; ^ создать структурный обработчик исключения; ^ генерировать исключения в программе. Продолжительность занятия - около 30 минут. Принципы обработки исключений Обнаружив ошибку периода выполнения, приложение генерирует исключение (exception). Исключения представляют собой экземпляры специализированных клас- сов, потомков класса System.Exception. Эти классы поддерживают функции, облега- ющие диагностику ошибок и управление их обработкой, например: • свойство Message — содержит описание ошибки в доступной для человека фор- ме и другие важные сведения об ошибке; • свойство StackTrace — хранит данные трассировки стека; обеспечивает трасси- ровку для поиска места в коде, где возникла ошибка. В дополнение к исключениям, определяемым системой, разрешается создавать пользовательские исключения на основе класса Sysfem.ApplicationException. Подроб- нее об этом — в следующих разделах. При возникновении ошибки периода выполнения создается экземпляр соответ- ствующего исключения и передается от метода, генерировавшего ошибку, вверх по стеку вызовов, В результате исключение получает метод, из которого был вызван тот, что сгенерировал ошибку. Если в этом методе имеется структура, способная обработать это исключение, оно обрабатывается; в противном случае оно передает- ся следующему методу в стеке вызовов и т.д. Если структура, обрабатывающая ис- ключения, не найдена в стеке вызова, вызывается обработчик исключений, задан- ный по умолчанию. Он отображает сообщение с указанием типа исключения и любых дополнительных сведений, предоставленных объектом исключения, и зак- рывает приложение, не позволяя сохранить результаты работы или исправить ошибку. Применение структурной обработки исключений позволяет приложению устра- нить последствия непредвиденных ошибок или, как минимум, сохранить данные перед закрытием. Создание обработчиков исключений Обработчики исключений реализуют для отдельных методов. У каждого метода дол- жен быть собственный обработчик исключений, ориентированный на особенности данного метода и приспособленный для обработки исключений, которые он обыч- но генерирует. Обработку исключений непременно следует реализовать в методах, которые часто генерируют исключения, например в использующих файловый до- ступ. Для обработчиков исключений принят синтаксис Try... Catch...Finally. Вот ос- новные этапы создания обработчика исключений: 1. поместите код, способный генерировать исключения, в блок Try (try); 2. добавьте один или несколько блоков Catch (catch), обрабатывающих различные типы исключений, которые может генерировать код в блоке Try (try)', 3. поместите в блок Finally (finally) код, который необходимо исполнить в любом случае. Этот код будет исполнен независимо от того, возникло ли исключение. Создание обработчика начинается с заключения некоторого кода в блок Try (try), формирующий обработчик исключений. Если при исполнении любого из операто- ров, заключенных в блок Try (try), возникнет исключение либо необработанное исключение будет передано из стека, метод попытается обработать его с помощью данного обработчика. Если это ему не удастся, исключение передается вверх но сте- ку вызовов. Вот пример блока Try (fry): Visual Basic .NET Try 1 Здесь находится обычный код приложения, а сюда помещают код для обработки ошибок, например блоки Catch 1 и Finally; оператор End Try завершает блок для обработки ошибок, End Try Visual C# // а код для обработки ошибок, например блоки Catch и Finally, // размещают вне фигурных скобок, отделяющих блок try. В Visual Basic .NET из блока Try выходят посредством оператора Exit Try. За блоком Try (try) может следовать один или несколько блоков Catch (catch), где находится код, который должен быть исполнен при обработке исключения. Блоки Catch (catch) бывают универсальными, перехватывающими любые исключения, либо специализированными, которые обрабатывают только определенный класс исключений, Ниже показан пример универсального блока Catch (catch), перехваты- вающего все исключения, генерированные в соответствующем блоке Try (try): Visual Basic .NET ' Здесь находится блок Try Catch ' Блок Catch завершается оператором End Try, другим блоком Catcft или блоком Finally. Код, размещенный в этих блоках, будет выполнен при возникновении ошибок периода выполнения. Visual C# // Здесь находиться блок Try catch { // Блок catch ограничен фигурными скобками. Код, заключенный между ними, // будет исполнен при возникновении ошибок периода выполнения, } Оператор Catch (catch) также позволяет получить ссылку на объект генериро- ванного исключения. Эта ссылка полезна, так как в обработчике ошибок через нее удается получить доступ к объекту исключения. Кроме того, она позволяет пере- хватывать определенные типы ошибок и создавать несколько блоков Catch (catch), предназначенных для обработки определенных ошибок, Так, например, можно пе- рехватывать исключение определенного типа: Visual Basic .NET 1 Здесь находиться блок Try Catch e As System.NullReferenceExceptior ' Если в блоке Try будет генерировано исключение NullReferenceException, этот блок Catch перехватит его. При этом в переменной е будет ссылка на объект исключения, открывающая доступ к сведениям об исключении. Catch e As System.Exception Поскольку все исключения являются производными от класса System.Exception, этот блок Catch будет перехватывать любые исключения, не перехваченные предыдущим блоком Catch. Visual C# // Здесь находиться блок Try catch (System.NullReferenceException e) { II Если в блоке Try будет генерировано исключение NullReferenceException, // этот блок Catch перехватит е^о. Пои этом в переменной е будет ссылка // на объект исключения, открывающая доступ к сеедениям об исключении. } catch (System.Exception e) { // Поскольку все исключения являются производными от класса // System.Exception, этот блок Catch будет перехватывать любые исключения, // не перехваченные предыдущим блоком Catch. } В блок Finally (finally) помещают код, который необходимо исполнить независи- мо от того, возникло исключение или нет. Это может быть код, сохраняющий дан- ные, освобождающий ресурсы или код, исполнение которого критично для прило- жения. Вот пример блока Try...Catch...Finally (try...catch..finally)'. Visual Basic .NET Public Sub Parse(ByVal aString As String) Try Dim aDouble As Double Если aString - Nothing, генерируется исключение ArgumentNullException aDouble - Double. Parse(aString) Catch e As System. ArgurnentNullException Здесь можно разместить код, который должен быть исполнен при возникновении исключения System. ArgumentNullException. Catch e As System. Exception Этот блок перехватывает все исключения, которые не были перехвачены предыдущим блоком. Finally Сюда помещают код, который необходимо исполнить независимо от того, возникло ли исключение или нет. End Try End Sub Visual C# oublic void Parse(string aString) { try { double aDouble; aDouble = Double. Parse(aString); } catch (System. ArgumentNullException e) { // Здесь можно разместить код, который должен быть исполнен при // возникновении исключения System. ArgumentNullException. } catch (System. Exception e) < // Этот блок перехватывает все исключения, которые не были // перехвачены предыдущим блоком. } finally { // Сюда помещают код, который необходимо исполнить // независимо от того, возникло ли исключение или нет. Если при исполнении кода, заключенного в блок Try (try), возникает ошибка, управление немедленно передается блокам Catch (catch). Далее все блоки Catch (catch) проверяются в поисках того, что перехватывает данное исключение. Ранее показаны два блока Catch (calch): один перехватывает только одно исключение (ArgumentNullException), а второй — все остальные. При возникновении ошибки ис- полняется только один блок Catch (catch); после исполнения кола в этом блоке упра- вление получает код из блока Finally (finally}. Далее возобновляется нормальное исполнение приложения со строки, следующей за строкой, откуда вызвана проце- дура, генерировавшая ошибку. Примечание Поскольку при обработке любого исключения исполняется только один блок Catch (catch), необходимо разместить блоки Catch (catch) так, чтобы пер- вым шел блок, перехватывающий наиболее специфичный тип исключений, а пос- ледним — блок, перехватывающий самый общий тип исключений. Таким образом, сначала следует разместить в коде блоки Catch (catch), обрабатывающие более спе- цифичные типы исключений, а после них — универсальную процедуру обработки ошибок. Иногда обрабатывать исключение при помощи локального обработчика неже- лательно, вместо этого лучше передать его по стеку методу, вызвавшему процедуру, при исполнении которой возникла ошибка. В этом случае тоже следует исполнить некоторый код при возникновении исключения. В подобной ситуации разрешено опустить блок Catch (catch) и использовать структуру Try...Finally (try..finally) следу- ющим образом: Visual Basic .NET Try Здесь находится код метода. Finally Здесь находится код, выполняющий очистку. End Try Visual C# try { // Здесь находится код метода. } finally { // Здесь находится код, выполняющий очистку. } > Создание обработчика исключений 1. Разместите в блоке Try (try) код, способный генерировать ошибки, которые нуж- но обработать. 2. Если исключения должны обрабатываться локально, создайте один или несколь- ко блоков Catch (catch) с соответствующим кодом. 3. Код, который необходимо исполнить независимо от возникновения исключе- ния, поместите в блок Finally (finally).