| « Поставить закладку » « Сделать стартовой » | |||
|
|||
|
Глава 7. Обработка ошибок в VB .NET
Глава №7. Обработка ошибок в VB .NET До настоящего
момента мы делали вид, что в наших программах ошибок не бывает. Но даже в самой
надежной программе иногда возникают непредвиденные ситуации — например, такие,
как потеря сетевого соединения или отсутствие бумаги в принтере. Конечно,
программист в этом не виноват, но винить пользователя в разрыве связи тоже было
бы несправедливо. По крайней мере, в таких ситуациях программа не должна
завершаться аварийно. Она должна:
Впрочем,
выполнить эти требования бывает непросто. Сетевой код обычно никак не связан с
объектами, состояние которых требуется сохранить. Соответственно, программисту
нужны средства для передачи управления и для оповещения других объектов о
происходящем, чтобы они могли принять нужные меры.
Так или иначе,
хороший программист знает, что наш мир не идеален, а непредвиденные ситуации
встречаются чаще, чем хотелось бы. В этой главе мы вернемся с небес на
землю. Итак, в отличие
от прежних версий VB в VB .NET поддерживается механизм структурной обработки
исключений (или просто обработки исключений). В этой главе вы не только
познакомитесь с синтаксисом обработки исключений в приложениях VB .NET, но и
узнаете, какими преимуществами обладает этот механизм. В частности, при
обработке исключений отпадает необходимость даже в более или ме- нее оправданном
применении GoTo, описанном в главе 3. Однако ничто хорошее не дается бесплатно,
поэтому вы также должны помнить о некоторых тонкостях, связанных со структурной
обработкой исключений.
Проверка ошибок и обработка
исключений Традиционный
механизм обработки ошибок, использовавшийся в прежних версиях VB, а также в
программировании СОМ и Windows, основан на проверке возвращаемого значения
функции и выборе действий. Обычно для проверки возвращаемого значения в
программе создается аналог конструкции Select Case, причем значения
интерпретируются абсолютно произвольно. Например, в одном случае 0 означает
успех, а в другом — неудачу. А в приведенном ниже фрагменте кода VB6 коды
выглядят и вовсе странно: Select Case Error-Number Case
57 MsgBox "Your printer may be off-line." Case
68 MsgBox "Is there a printer available?" ' Другие секции Case Case
Else ' Все остальные случаи End
Select Подобные
конструкции работают, но их трудно читать и еще труднее изменять в процессе
сопровождения программы. Можно уверенно сказать, что эта схема таит в себе
широкие возможности для ошибок программирования. Например, вы можете перепутать
коды ошибок или забыть проверить некоторые из возвращаемых значений. Кроме того,
писать один и тот же код проверки при каждом вызове функции Windows API, по
крайней мере, утомительно. Хотя в некоторых ситуациях возвращаемое значение
приходится проверять независимо от выбранной схемы обработки ошибок, не стоит
превращать это в постоянную практику. Также следует учитывать фактор
эффективности: структурная обработка исключений быстрее программируется,
отнимает меньше времени при сопровождении, а нередко и выполняется
быстрее! Подготовка к структурной обработке исключений Прежде чем
переходить к примерам, демонстрирующим обработку исключений на практике,
необходимо познакомиться с некоторыми обстоятельствами. Во-первых, при
структурной обработке исключений в программу включается дополнительная ветвь,
которая автоматически выполняется при возникновении каких-либо аварийных ситуаций. Кроме того,
при обработке исключений VB .NET автоматически создает объект, содержащий
информацию об ошибке. Когда в
программе происходит исключение, встроенный механизм начинает искать обработчик,
подходящий для данного объекта исключения (то есть для конкретной причины
ошибки). Речь идет не о наборе GoTo, запутывающих логику программы, — обработка
исключения больше напоминает запасную дорогу, идущую параллельно главной
магистрали и связанную с ней несколькими переездами — настоящей мечте любого
водителя, попавшего в пробку. Если в программе что-то пойдет не так, управление
автоматически передается ветви, содержащей логику обработки исключений (если,
конечно, вы ее запрограммировали). После этого исключение либо рассматривается
одним из обработчиков, либо передается дальше по цепочке. В VB .NET для
обработки исключений существует синтаксическая конструкция, называемая блоком
Try-Catch. Допустим, у нас имеется консольное приложение ProcessFile.
Предполагается, что пользователь запускает его в режиме командной строки
командой вида ProcessFile имя_файла Имя файла
передается в виде параметра. Как это обычно бывает, пользователи будут делать
все, чтобы сбить бедную программу с толку. В частности, они
могут:
Программа должна
быть написана так, чтобы учитывать все возможные ошибки со стороны пользователя.
Ниже приведен пример простого блока Try-Catch, который может входить в
приложение ProcessFile: Module Exceptionl
Sub Main() Dim args() As
String Try args = Environment.GetCommandLineArgs() ProcessFile(argsd)) Catch Console.WriteLine("ERROR") End
Try Console.WriteLine("Press enter to end") Console. ReadLine() End Sub
Sub ProcessFiletByVal fileName As String) ' Обработка
файла Console.WriteLine("Am processing " & fName) End Sub End
Module Секция Try блока
Try-Catch содержит «правильный» код — в данном примере это вызов ProcessFile
(вызов Environment.GetCommandLingArgs() заключен в секцию Try, потому что он
тоже может инициировать исключение — например, если ваша программа работает на
платформе, не поддерживающей передачи аргументов в командной
строке). Секция Catch в
блоке Try-Catch необходима, потому что некоторые невнимательные пользователи не
обращают внимания на указания. Если в приведенном фрагменте пользователь
забывает ввести имя файла, программа пытается обратиться к имени файла, что
приводит к исключению IndexOutOfRangeExceptl on, поскольку элемент с указанным
индексом отсутствует в файле. При возникновении исключения управление передается
в дополнительную ветвь, то есть в блок Catch, который в нашем примере просто
выводит строку ERROR в консольном окне.
Следующим шагом
должен быть перехват и последующий анализ исключения. Для этого команда Catch
приводится к следующему виду: Catch excep As
Exception (имя может быть
любым, поскольку упоминание в заголовке секции Catch считается объявлением
переменной). Теперь объект исключения, на который ссылается ехсер, автоматически
заполняется данными. Например, в следующей секции Catch используется встроенный
метод ToString объекта исключения ехсер: Catch ехсер As
Exception Console.WriteLine(excep) Результат
выглядит примерно так: System.IndexOutOfRangeException: An exception of type_ System.IndexOutOfRangeException was thrown, at Exception_l.Exception!.Main() in C:Documents and_
Settingsx20My DocumentsWisual Studio ProjectsConsoleApplication!4Exception.vb:1ine 6 Из описания
видно, что ошибка произошла в строке 6 при обращении к элементу массива.
Конечно, если вы не хотите пугать пользователя, выводить эту информацию в
окончательной версии программы не рекомендуется, но в процессе отладки она очень
полезна. При знакомстве с
этим примером возникает логичный вопрос. Допустим, пользователь ввел имя файла,
но метод ProcessFile не может его обработать. Что тогда? Можно ли отличить одно
исключение от другого? Как вы вскоре увидите, небольшое усложнение секции Catch
позволяет различать исключения по категориям. Более того, в секции Catch можно
даже заново инициировать перехваченное исключение командой Throw, чтобы
продолжить его обработку. Наличие нескольких секций Catch Одной секции Try
в VB .NET может соответствовать несколько секций Catch. Каждая секция
перехватывает определенную категорию исключений, при этом для идентификации
ошибок используются объекты классов, производных от базового класса Exception.
Пример: Sub
Main() Dim args().
argument As String Try args =
Environment.GetCormandLineArgs() ProcessFile(argsd)) Catch indexProblem
As IndexOutOfRangeException Console.WriteLine("ERROR - No file name supplied") Catch ioProblem As
System.10.I0Exception Console.WriteLine("ERROR - can't process file named " & args(D) Catch except As
Exception ' Прочие исключения End
Try Console.WriteLine("Press enter to end") Console. ReadLine() End
Sub В данном примере
программа последовательно просматривает все секции Catch, пытаясь найти
совпадение. Если пользователь не указал имя файла, совпадение будет найдено в
первой секции. Вторая секция должна совпадать в том случае, если при вызове
ProcessFile не удастся обработать файл (возможные причины рассматриваются
далее). Если первые два случая не подошли, остальные исключения перехватываются
последней секцией Catch.
Совпадение
считается обнаруженным, если текущее исключение относится к типу, указанному в
заголовке секции Catch, или производному от него. Например, класс
FileNotFoundException является производным от I0Exception, поэтому следующий
фрагмент неправилен: Try ProcessFile(args(1)) Catch indexProblem
As IndexOutOfRangeException Console.WriteLinet"ERROR = No file name supplied") Catch IOProblem As
System.IO.l0Exception Console. WriteLinet "ERROR = can't process file named " & args(D) Catch fileNotFound As System.IO.FileNotFoundException End
Try Специализированное исключение FileNotFoundException будет поглощено
предыдущей секцией, перехватывающей исключение базового класса
l0Exception. Из сказанного
следует, что размещать секции Catch после секции Catch e As Exception
бесполезно. Указание типа Exception в первой секции Catch автоматически
перекрывает все остальные секции (кстати говоря, секция Catch без явного
указания типа исключения считается эквивалентной Catch e As Exception). Также
следует учитывать, что пустая секция с условием Catch e As Exception напоминает
очень опасную конструкцию On Error Resume из прежних версий VB. Несмотря на все опасности, связанные с перехватом обобщенных исключений Catch e As Exception, эту проверку рекомендуется включать в последнюю секцию Catch любого блока Try — особенно на стадии разработки и тестирования, поскольку эта проверка помогает лучше изолировать ошибки. Если все остальные способы не помогают, попробуйте вывести содержимое стека на консоль или в файл методом StackTrace класса обобщенного исключения Exception. Пример: Try Catch indexProblem
As IndexOutOfRangeException Catch fnf As
System.I0.FileNotFoundException Catch ioProblem As
System.I0.lOException Catch e As
Exception "of this
message") End Try
Catch badnameException When theName - String.Empty Выше уже
говорилось о том, что метод ProcessFilе просто передает исключение в процедуру
Sub Main, из которой он был вызван. В процедуре Sub Mai n команда вызова тоже
заключена в блок Try-Catch, поэтому исключение будет обработано. С другой
стороны, такое решение выглядит немного наивно, а если написанные вами классы
будут использоваться другими программистами, оно становится попросту опасным. Но
даже если дело как-нибудь обойдется, пользователи вашего кода вряд ли будут
довольны тем, что вы без разбора передаете исключения, не пытаясь их
обработать. Лучше попытаться
по возможности «прибрать» за собой, а затем воспользоваться ключевым словом
Throw, чтобы передать объект исключения вызывающей стороне. В главе 4
упоминалось о том, что в VB .NET не поддерживается детерминированное завершение.
Следовательно, если вы создали объект с методом D1 spose, этот метод следует
вызвать перед тем, как инициировать исключение. Сказанное относится и к открытию
файлов, и к получению графического контекста. В следующем фрагменте представлена
условная структура подобного кода: Try ' Создание
локального объекта с методом Dispose ' Код. который может инициировать исключения Catch(e As
Exception) local
Object.dispose() Throw e; End Try Если вы не
вызовете метод Dispose для своего локального объекта, то захваченные ресурсы так
и не будут освобождены. Ведь ссылка на объект существует лишь в локальном коде;
остальные части программы не обладают доступом к методу Dispose! С другой
стороны, причина, по которой возникло исключение, остается в силе, поэтому о
возникшей проблеме (например, о неудачной операции с файлом) нужно сообщить
вызывающему коду. Для этого следует заново инициировать исключение командой
Throw, как это сделано во второй выделенной строке. Впрочем, если вы
действительно хотите программировать «как положено», не ограничивайтесь простым
перезапуском исключения. Постарайтесь сделать свой код как можно более
информативным и включите в объект исключения дополнительную информацию. Для
этого есть три возможности.
Решения
расположены по возрастанию приоритета, и в идеальном случае следует всегда
использовать пункт 3. На практике программисты при выборе руководствуются своей
оценкой того, какую информацию об исключении необходимо передать для дальнейшей
обработки. Для примера
представьте такую ситуацию: из источника данных читаются пары «ключ/значение», и
для последнего ключа не находится парного значения. Программа предполагает, что
значение ассоциируется с каждым ключом, поэтому при попытке чтения возникает
неожиданно'е исключение ввода-вывода (чтение данных из файла описано в главе 9).
Теперь вы хотите сообщить о происходящем вызывающей стороне. Чтобы добавить в исключение строку, можно воспользоваться специальной версией конструктора класса Exception: Public Sub
New(ByVal message As String) В следующем
фрагменте в объект IOException добавляется новая строка с сообщением об
отсутствии значения для последнего ключа, после чего исключение инициируется
заново. Dim excep As New
IQException("Missing value for last key") Throw excep Получив
инициированное исключение, внешний код получает текст сообщения методом Message
класса Exception и узнает о возникшей проблеме.
Вторая ситуация
реализуется элементарно благодаря главному правилу наследования: производный
класс всегда может использоваться вместо базового класса. Вам лишь остается
инициировать исключение производного класса, которое лучше подходит для данной
ситуации. Последний случай
требует некоторой дополнительной работы, поскольку для этого потребуется
определить класс, производный от существующего класса исключения. Предположим,
вы хотите определить новый класс исключения, производный от System. 10.
lOException. Новый класс отличается от старого лишь одним ReadOnly-свойством,
возвращающим ключ, с которым не ассоциируется парное значение: Public Class LastValueLostException Inherits System.I0.I0.Exception Private mKey As String Public Sub
New(ByVal theKey As String) MyBase.New("No value found for last key") mKey = theKey End
Sub Public Readonly
Property LastKey() As String Get Return mKey End Get End Property End
Class Обратите
внимание: имя созданного класса исключения завершается словом Exception. Это
стандартное правило, которому мы настоятельно рекомендуем следовать. Получив
исключение LastValueLostException, программист может воспользоваться свойством
LastKey, значение которого передается в конструкторе нового класса исключения, и
получить ключ, не ассоциируемый со значением. Следующая строка обеспечивает
выдачу правильной информации методом Message базового класса Exception:
MyBase.New("No value found for last key") В этой строке
вызывается конструктор базового класса (и в конечном счете конструктор предка
Exception). Возможно, вы
заметили, что в классе LastValueLostException не переопределяются другие методы
— такие, как метод ToString, унаследованный от Exception. В стандартных
ситуациях объекты исключений всегда должны выводить стандартные
сообщения. Как использовать
созданный класс в программе? Например, если последний ключ без парного значения
был равен «oops», исключение будет инициироваться следующей
командой: Throw New
LastValueLostException("oops") Мы создали новый
класс исключений, производный от IOExcepti on, потому что потенциальная проблема
явно относилась к категории ввода-вывода. Допустим, ситуация имеет более общий
характер и для базового класса не существует других очевидных кандидатов, кроме
класса Exception. Впрочем, это не совсем верно — лучший выбор существует всегда.
Мы настоятельно рекомендуем выбирать в качестве базового не сам класс Exceptlon,
а производный от него класс AppllcationException. Дело в том, что
.NET Framework различает исключения, возникшие в результате проблем
исполнительной среды (например, нехватки памяти или дискового пространства) и
проблем, обусловленных работой вашего приложения. Именно исключения второй
категории должны быть производными от AppllcationExcepti on, поэтому именно этот
класс следует выбирать базовым при определении обобщенных исключений в
программе.
Исполнительная
среда помогает сделать следующий шаг. Иерархия исключений расходится на две
ветви, показанные на рис. 7.1.
Рис. 7.1.
Две основные ветви иерархии исключений Классы
Exceptlon, AppllcationExcepti on и SystemExcepti on обладают одинаковой
функциональностью. Существование трех классов вместо одного — не более чем
удобная абстракция, благодаря которой становится проще понять исключения,
возникающие в ваших программах. Исключения как замена для goto Обработка
исключений в сочетании с определением собственных классов исключений позволяет
полностью отказаться от использования GoTo. Например, в главе 3 был приведен пример
оправданного применения GoTo для прерывания вложенных циклов, когда ошибка
происходит во внутреннем цикле. Программист VB .NET в подобной ситуации просто
заключает весь цикл в блок Try-Catch, как показано ниже: Sub
Main() Dim getData As
String Dim i, j As
Integer Dim e As
System.I0.I0Exception Try For i = 1 To 10 For j = 1 To 100
Console.WriteC'Type the data, hit the Enter key between " & _ "ZZZ to end: ") getData _ Console.ReadLine()
If getData = "ZZZ" Then e New
System.I0.I0Exception("Data entry ended " & _ "at user request")
Throw e Else ' Обработка данных End If Next j Next i Catch Console.WriteLinete.Message) Console. Readline() End Try End Sub В приведенном
выше фрагменте выделенные строки нельзя объединить конструкцией следующего
вида: Dim e As New
System.IO.IOException("Data entry ended at user request") Вследствие
правил видимости VB .NET объект исключения окажется недоступным в секции
Catch. При
использовании блоков Try-Catch нередко существует код, который должен
выполняться как при нормальном завершении, так и при возникновении исключения.
Например, в обоих случаях следует закрыть файлы, вызвать методы Dispose и т. д.
Даже в простом примере, приведенном в начале главы, потребовалась команда
ReadLine, чтобы консольное окно оставалось на экране до нажатия клавиши
Enter. Чтобы некоторый
фрагмент выполнялся независимо от того, возникнет ли в программе исключение или
нет, в блок Try-Catch включается секция Finally, выделенная в следующем примере
жирным шрифтом: Sub
Main() Dim args().
argument As String args = Environment.
GetCommandLineArgs() Try ProcessFile(argsd)) Catch Console.WriteLine("ERROR") Finally Console.WriteLine("Press enter to end") Console.ReadLine() End Try End Sub
Рекомендации по использованию
исключений Исключения
выглядят эффектно, и новички часто склонны злоупотреблять ими. В самом деле,
стоит ли тратить время на анализ пользовательского ввода, когда можно просто
инициировать исключение? Не поддавайтесь соблазну. При неправильном
использовании обработка исключений существенно замедляет работу программы. Ниже
приведены некоторые рекомендации по использованию исключений в
программе.
HTML 5: пять вещей вызывающих особый интер....
HTML 5 — это грядущее обновление гипертекстового языка разметки, основного способа создания контента для размещения его во всемирной паутине. Разработка HTML остановилась в 1999 году, на версии HTML 4.01 и с тех пор web-содержимое изменилось так, что текущие спецификации HTML перестали соответствовать сегодняшним требованиям. HTML 5 нацелен на то, чтобы увеличить функциональную совместимость HTML и соответствовать растущим требованиям разнообразного и смешанного web-контента. HTML 5 так же нацелен на устранение недостатков четвертой версии. В этой статье мы взглянем на 5 новых интересных вещей в HTML 5.
Подробнее... |
Рубрика: Html
| Добавлено: 22.12.2008
asp.net: ListView с разных сторон.
Элемент управления ListView был представлен в .Net Framework 3.5 как замена устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его предшественник, но в тоже время лишен некоторых внутренних механизмов, что впрочем целиком следствие из расширенной универсальности ListView. Среди отличий ListView и GridView можно назвать и гибкую настройку разметки, что позволяет выводить данные не только в табличном виде, но и вообще в любом каком пожелает программист. Благодаря шаблонам ItemTemplate, EditItemTemplate, InsertItemTeplate можно настроить внешний вид при любом из состояний ListView: редактировании или выборе элемента.
Подробнее... |
Рубрика: .NET компоненты
| Добавлено: 22.12.2008
Создание кросс-таб отчета в Stimulsoft Rep....
Компания Стимулсофт предоставляет для разработчиков мощный набор инструментов для создания отчетов для Microsoft Visual Studio .Net 2005 и 2008; эти инструменты доступны как для Windows Forms, так и для Web Forms. Это генератор отчетов Stimulsoft Reports.Net. Генератор отчетов Stimulsoft Reports.Net имеет ряд особенностей: простая работа с дизайнером отчетов, полная поддержка экспорта в PDF, Word, Excel и многие другие форматы. Crystal Report и Microsoft Reporting Service – очень хорошие программные продукты для повседневной работы, но, если Вам необходимо создать отчеты с поддержкой кросс-табов, drill down, Ajax, штрих-кодов и возможностью подключения одновременно более одного источника данных, то Stimulsoft Reports.Net поможет Вам сэкономить массу времени. Также, данный генератор отчетов позволяет пользователям создавать свои собственные отчеты любой сложности. И все эти особенности делают Stimulsoft Reports.Net хорошим выбором в сфере программных продуктов для Business Intelligence.
Подробнее... |
Рубрика: .NET компоненты
| Добавлено: 22.12.2008
Остальные статьи: |
Цитата дня (все,добавить):
|
Realcoding.NET
© 2003-2008 |
Контакты |
Реклама на сайте
|