Обработка ошибок в VB .NET

До настоящего момента мы делали вид, что в наших программах ошибок не бывает. Но даже в самой надежной программе иногда возникают непредвиденные ситуации — например, такие, как потеря сетевого соединения или отсутствие бумаги в принтере. Конечно, программист в этом не виноват, но винить пользователя в разрыве связи тоже было бы несправедливо. По крайней мере, в таких ситуациях программа не должна завершаться аварийно. Она должна:

Впрочем, выполнить эти требования бывает непросто. Сетевой код обычно никак не связан с объектами, состояние которых требуется сохранить. Соответственно, программисту нужны средства для передачи управления и для оповещения других объектов о происходящем, чтобы они могли принять нужные меры.

Непредвиденные ситуации возникают и в хорошо написанных программах, но в плохо написанных программах они возникают чаще. Например, если данные не проверяются перед использованием, может возникнуть ошибка деления на 0 или переполнение контейнера, не рассчитанного на такой объем данных. Вы как программист обязаны проследить за тем, чтобы этого не случилось. Какую бы форму обработки ошибок вы ни избрали, она все равно не заменит проверки данных перед использованием!

Так или иначе, хороший программист знает, что наш мир не идеален, а непредвиденные ситуации встречаются чаще, чем хотелось бы. В этой главе мы вернемся с небес на землю.

Итак, в отличие от прежних версий VB в VB .NET поддерживается механизм структурной обработки исключений (или просто обработки исключений). В этой главе вы не только познакомитесь с синтаксисом обработки исключений в приложениях VB .NET, но и узнаете, какими преимуществами обладает этот механизм. В частности, при обработке исключений отпадает необходимость даже в более или ме-

нее оправданном применении GoTo, описанном в главе 3. Однако ничто хорошее не дается бесплатно, поэтому вы также должны помнить о некоторых тонкостях, связанных со структурной обработкой исключений.

Читатели, привыкшие к синтаксису прежних версий VB, могут продолжать использовать старую конструкцию On Error. Но в новых программах это выглядит довольно глупо. Давно пора отказаться от архаичного способа обработки ошибок, появившегося на заре развития вычислительной техники! (Одновременное использование двух способов в одной процедуре не разрешается.)

 

Проверка ошибок и обработка исключений

Традиционный механизм обработки ошибок, использовавшийся в прежних версиях 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 в консольном окне.

Из блока Try, как и из других управляющих конструкций VB .NET (таких, как циклы For и Do), можно выйти немедленно командой Exit Try. Впрочем, применение Exit Try обычно считается проявлением плохого стиля программирования.