Анализ
исключений
Следующим
шагом должен быть перехват и последующий анализ исключения. Для этого команда
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_ Settings\x20\My DocumentsWisual Studio
Projects\ConsoleApplication!4\Exception.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, VB выполняет ее. Код других секций 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
ProcessFile(argsd))
Catch indexProblem
As IndexOutOfRangeException
Console.WriteLine("ERROR - No file name supplied")
Catch fnf As
System.I0.FileNotFoundException
Console.WriteLinet"ERROR - FILE NOT FOUND")
Catch ioProblem
As System.I0.lOException
Console.WriteLine("ERROR - can't process file named " & args(1))
Catch e As Exception
Console.WriteLinet"Please inform the writer of this program " &
_
"of this
message")
Console.Writete.StackTrace)
End Try
Что произойдет, если возникшее исключение не подойдет ни к одной из секций Catch,
а в конце блока Try-Catch отсутствует универсальная секция Catch e As Exception?
В этом случае исключение передается в секцию Try верхнего уровня, заключающую
код внутренней секции Try. Если подходящая секция Catch не будет найдена и во
внешней секции Try, поиск продолжается в методе, от которого поступил вызов.
Вероятно, именно это и произойдет при вызове метода ProcessFi I e из предыдущего
примера — метод ProcessFi 1е передает все необработанные исключения (в форме
объекта Exception) в процедуру Sub Main.
Если исключение не будет перехвачено ни одной секцией Try в методе, управление переходит в секцию Finally, а затем немедленно передается за пределы метода. Таким образом, обработку исключений можно рассматривать как невероятно мощную (и притом интеллектуальную) разновидность GoTo. Интеллектуальность заключается в автоматическом выполнении завершающего кода в секции Finally.
В общем случае, если исключение не было обработано программой вплоть до точки
входа в приложение, .NET выводит сообщение с описанием исключения и содержимое
стека с информацией обо всех вызванных методах на момент возникновения исключения.
В VB .NET секция Catch может дополняться условием When, расширяющим возможности
ее применения. Синтаксис выглядит следующим образом:
Catch badnameException When theName - String.Empty