Анализ исключений

Следующим шагом должен быть перехват и последующий анализ исключения. Для этого команда 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