Обработка ошибок в VB.NET
Автор: Evangelos Petroutsos
Mastering™ Visual Basic® .NET
San Francisco London
Copyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com
Перевод: Игорь Шувалов aka
Ronin
Написание программного обеспечения, даже самого маленького,
может быть очень трудной задачей. Разработчики обычно тщательно обдумывают и
планируют задачи, и методики, используемые для решения этих задач. Комплексная
разработка ПО постоянно ведет к ошибкам в программировании. Эта статья создана
для того, чтобы познакомить вас с описанием различных типов ошибок, которые вы
можете случайно встретить в процессе программирования на Visual Basic .NET,
некоторыми инструментами, которые вы можете использовать для обнаружения ошибок,
и о специальных структурах программирования, применяемых для предупреждения
ошибок в работающей программе.
Вдобавок к ошибкам, ваше приложение должно быть готово гибко обработать все
неправильные условия, от тех, которые могут произойти по вине пользователей
(когда, например, вводят строку вместо числового значения или даты) до
неисправных устройств, или простейших ситуаций, связанных с невозможностью
записать данные в файл из-за использования этого файла другим пользователем. Все
эти условия могут не коснуться вашей программы, но для этого ваше приложение
должно специальным образом их обработать. Ваше приложение может показывать
предупреждения и сообщения об ошибках, но ни в коем случае не должно завершиться
крахом.
Ошибки, вызываемые компьютерной программой (не важно на каком
языке она написана), могут быть разделены на три основных типа: Design-time (во
время проектирования), Runtime (во время выполнения) и Logic (логические).
Design-time ошибки легче всего обнаружить и исправить. Такого рода ошибки
возникают при написании кусков кода, которые не удовлетворяют правилам того
языка, на котором они пишутся. Их легко найти, потому что Visual Studio .NET не
только указывает на область их возникновения, но и на конкретное выражение.
Runtime ошибки обнаружить сложнее, потому что VS не дает никакой помощи в их
обнаружении до непосредственного возникновения их в программе. Они возникают при
попытке выполнить недопустимое действие, например, получить доступ к данным,
которые не существуют, или к ресурсам, к которым приложение не имеет прав
доступа. При возникновении такого рода ошибок, если они не будут обработаны
должным образом, приложение может либо закончиться крахом, либо зависнуть.
Третий тип ошибки, логические, являются самыми коварными и сложными в
обнаружении, т.к. они не оказывают никакого влияния на работоспособность
приложения. Наличие в программе логических ошибок означает лишь то, что вы
будете получать неверные результаты от работы программы. Это могут быть
простейшие ошибки при неверной калькуляции, или пункт меню, который вы забыли
установить в положение disabled и т.д.
Runtime ошибки в Visual Basic .NET вызывают исключения.
Исключение – это реакция на ошибку, сгенерированную приложением. При
возникновении исключения появляется диалог, сообщающий об ошибке с
дополнительной информацией. Этот диалог дает возможность пользователю продолжить
выполнение программы. В редких случаях это может быть полезным, но чаще всего
это не желательно. Продолжение выполнения программы может повлечь за собой
неисправимые ошибки, например записи в базу данных неверных (ошибочных) данных.
Если мы не хотим, чтобы пользователь мог обрабатывали исключения, то мы можем
сами легко обрабатывать их программно. Модель обработки ошибок в Visual Basic .NET
позволяет сделать это очень легко. Обработчик ошибок в VB.NET – это блок кода,
который может обнаружить исключения и выполнить необходимые действия для
исправления этой ошибки. Вот примеры ошибок.
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Dim s As String
s = "answer"
Button2.Text = s.Substring(10, 1)
End Sub
Этот код пытается отобразить одиннадцатый символ строки s.
Как вы видете, строка s содержит только 6 символов, что и приведет к ошибке.
Давайте посмотрим точное определение исключения:
An unhandled exception of type ‘System.ArgumentOutOfRangeException’ occurred in
mscorlib.dll
Additional information: Index and length must refer to a location within the string.
Первое, что помогает нам убедиться в том, что это runtime
ошибка – это слово unhandled. Это значит, что ошибка была в строке, не входящей
в блок обработчика исключений. Еще один интересный момент, это тип вызванного
исключения System.ArgumentOutOfRangeException. Очень важно знать то, что разные
типы ошибок разделяются в группы. Это важно, когда вы реализуете механизм
обработки ошибок. Исключения создают объект, наследуемый от класса Exception.
Блок “Additional information” («дополнительная информация»), дает нам некоторую
специфическую информацию о природе возникновения ошибки.
Давайте приведем пример обработки ошибок на примере
предыдущего кода:
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Dim s As String
s = "answer"
Try
Button2.Text = s.Substring(10, 1)
Catch
Button2.Text = "error"
End Try
End Sub
Это тот-же код, но теперь выражение, вызывающее ошбку
помещено в блок обработки исключений Try…Catch…End Try. Этот блок представляет
собой простейший обработчик исключения. Если в блоке Try генерируется
исключение, то программа автоматически переходит в блок Catch. Если никаких
исключений не генерируется, то программа пропускает блок Catch. При выполнении
этого кода, происходит исключение System.ArgumentOutOfRangeException, но
программа продолжает выполняться и не показывает никаких сообщений. После
выполнения свойство Text кнопки Button2 будет иметь значение "error".
В следующем примере, исключение генерирует обьект ex класса Exception.
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Dim s As String
s = "answer"
Try
Button2.Text = s.Substring(10, 1)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Очень полезно иметь этот объект. Он позволяет нам получить
всю информацию об исключении. Вместо MsgBox в реальной программе можно,
например, вести лог ошибок в специальный журнал ошибок.
Заметьте, что в предыдущем коде исключения не разделяются на типы. То есть при
любом исключении будет обрабатываться один и тот же кода обработчика. Вы также
можете написать специфические обработчики ошибок для разнообразных типов
исключений, как в следующем далее.
Private Sub Button3_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button3.Click
Try
Button3.Text = lbStates.SelectedItem.ToString
Catch ex As System.NullReferenceException
MsgBox("Please select an item first")
Catch ex As Exception
MsgBox("Some other error: " & ex.Message)
End Try
End Sub
Этот код пытается получить выбранный элемент из ListBox’a
lbStates. Если в lbStates не выбрано ни одного элемента, то генерируется
исключение System.NullReferenceException. Это исключение обрабатывается
специальным обработчиком, который вызовет MsgBox с соответствующим сообщением ("Please
select an item first"). Если же произойдет исключение какого-либо другого типа,
то оно будет обработано другим обработчиком.
Заметьте, что в предыдущем коде обработчик специфичного типа исключений стоит
ранее обработчика общего типа. Это определяет порядок обработки исключений.
Также заметьте, что объект ex присутствует в каждом обработчике. Это становится
возможным, потому, что это объявление несет локальный характер, и область
видимости этого объекта ограничивается соответствующим блоком Catch.
При использовании структуры обработки исключений Try…Catch…End
Try исключение обрабатывается первым релевантным обработчиком. После этого
программа уходит за рамки этой структуры. Иногда требуется выполнить какой-либо
код, перед тема как выйти из структуры Try…Catch…End Try. Как, например, в
следующем коде.
Protected Sub ReadFromATextFile(ByVal cFilename As String)
Dim s As StreamReader
Dim cLine As String
Dim bDone As Boolean = False
lbresults.Items.Clear()
s = New Streamreader(cFilename)
Try
While Not bDone
cLine = s.ReadLine()
If cLine Is Nothing Then
bDone = True
Else
lbresults.Items.Add(cLine)
End If
End While
s.Close()
Catch ex As Exception
MsgBox("some error occurred")
End Try
End Sub
В этом коде читается файл и построчно загружается в ListBox.
Если возникнет исключение в цикле, то мы не сможем закрыть Streamreader (вызвать
s.Close()), что может повлеч к ненужной трате ресурсов.
К счастью, для этого существует специальный блок, код которого будет выполнен в
любом случае. Этот блок называется Finally. Вот измененный код из предыдущего
примера.
Protected Sub ReadFromATextFile(ByVal cFilename As String)
Dim s As StreamReader
Dim cLine As String
Dim bDone As Boolean = False
lbresults.Items.Clear()
s = New Streamreader(cFilename)
Try
While Not bDone
cLine = s.ReadLine()
If cLine Is Nothing Then
bDone = True
Else
lbresults.Items.Add(cLine)
End If
End While
s.Close()
Catch ex As Exception
MsgBox("some error occurred")
Finally
s.Close()
End Try
End Sub
Теперь в любом случае Streamreader будет закрыт и ресурсы
будут высвобождены.
При создании собственных классов, вы, вероятно, встретитесь с
необходимостью вызова исключений. Это сделать очень просто – для этого
существует ключевое слово Throw. Разработчик, использующий ваш класс, сможет
обработать это исключение. Вот пример вызова (срабатывания) исключения.
Private FValue As Integer = 0
Property Value() As Integer
Get
Return FValue
End Get
Set(ByVal iValue As Integer)
If iValue <= FMax Then
FValue = iValue
Else
FValue = FMax
Throw New OverflowException(_
“Cannot set ProgressBar value to greater than maximum.”)
End If
Invalidate()
End Set
Здесь вызывается исключение OverflowException с вашими
комментариями.