Обработка ошибок
Пример обработки ошибок можно видеть в коде программы просмотра текстовых файлов, рассмотренной в предыдущей статье, где существует вероятность возникновения проблемы при открытии заданного файла.
private void open_file( string filename )
{
try
{
string text;
size_t size;
FileUtils.get_contents( filename, out text, out size );
this.text_view.buffer.set_text( text, (int) size );
}
catch( Error e )
{
stderr.printf( "Ошибка: %s\n", e.message );
}
}
В конструкции try {} catch {}
не видно никаких отличий от аналогов из C++, C# и Java. Особенности обнаруживаются на более низком уровне – в реализации. В библиотеке GLib имеется средство управления исключениями времени выполнения GError. Vala преобразует его в более привычную "современную" форму, но сам по себе GError изначально был предназначен для обработки так называемых восстановимых ошибок времени выполнения, о которых ничего не известно до начала выполнения, и которые не приводят к аварийному завершению программы. Поэтому не следует напрямую использовать GError для тех исключительных ситуаций, которые можно "предсказать", например, передача отрицательного числа в функцию, которая требует параметр только со значением, большим нуля.
При возникновении необходимости в написании специфического кода для обработки исключений общая схема работы точно такая же, как при использовании любого другого языка из "C-группы". Все функции, являющиеся потенциальными источниками исключений, определяются с ключевым словом throws. Везде, где это необходимо в коде, записываются команды генерации исключений throw. Вызовы потенциально "опасных" функций помещаются в блоки try-catch.
Отличия проявляются при определении типов обрабатываемых исключений (ошибок). Исключения обладают тремя характеристиками: домен (domain), код (code) и сообщение (message). Домен определяет тип ошибки. Ближайший аналог – подкласс класса Exception в языке Java. Каждый домен исключения может содержать один или несколько кодов ошибок:
errordomain IOError // имя домена
{
FILE_NOT_FOUND // код ошибки
}
Таким образом, можно определить не только обобщённый тип возникающей проблемы, но и конкретную ошибку. Что касается сообщений, то мы уже встречались с ними – это текст, выводимый при возникновении исключительной ситуации.
Для того чтобы обрабатывать исключения в программе, вы должны определить домены с кодами ошибок и написать соответствующие обработчики для доменов. Каждому домену ошибок соответствует отдельный блок catch. Кроме того, после блока try и всех необходимых блоков catch можно добавить необязательный блок finally, который будет выполняться всегда, даже при отсутствии ошибок и их обработки, потому что иногда требуется освобождение ресурсов, захваченных внутри блока try.
Всё, сказанное выше, можно проиллюстрировать следующим примером:
errordomain ErrType01
{
ERROR_CODE_01
}
errordomain ErrType02
{
ERROR_TYPE_02
}
public class MyClass : GLib.Object
{
public static void myfunc_throw() throws ErrType01, ErrType02
{
throw new ErrType01.ERROR_CODE_01( "Ошибка с кодом 01" );
}
public static void myfunc_catch() throws ErrType02
{
try
{
myfunc_throw();
}
catch( ErrType01 e )
{
// Здесь обрабатываются ошибки из домена ErrType01
}
finally
{
// Освобождение ресурсов и прочие необходимые операции
}
}
public static int main( string[] args )
{
try
{
myfunc_catch();
}
catch( ErrType02 e )
{
// Здесь обрабатываются ошибки из домена ErrType02
}
return 0;
}
}
Здесь метод myfunc_throw может генерировать исключения из обоих доменов, определённых в программе. Метод myfunc_catch способен генерировать исключения только второго типа, поэтому обязан обеспечить обработку ошибок из домена ErrType01. В свою очередь, метод main обеспечивает обработку всех ошибок, которые могут возникать при работе метода myfunc_catch.
Last updated
Was this helpful?