Vala编程语言高级特性-错误处理
错误处理
GLib 有一个用于管理运行时异常的系统,称为 GError。Vala 将其转换为现代编程语言熟悉的形式,但其实现意味着它与 Java 或 C# 中的不完全相同。重要的是要考虑何时使用这种类型的错误处理——GError 是专门设计用来处理可恢复的运行时错误的,即那些在程序在实时系统上运行之前未知、且对执行不是致命的问题。你不应该将 GError 用于可以预见的问题,例如报告向方法传递了无效值。例如,如果一个方法要求参数是大于 0 的数字,它应该使用契约编程技术(如前一节描述的前置条件或断言)来处理负值。
Vala 错误是所谓的受检异常,这意味着错误必须在某个时刻得到处理。但是,如果你不捕获错误,Vala 编译器只会发出警告而不会停止编译过程。
使用异常(或 Vala 术语中的错误)涉及以下几个方面:
声明一个方法可能引发错误:
void my_method() throws IOError {// ...
}
在适当时抛出错误:
if (something_went_wrong) {throw new IOError.FILE_NOT_FOUND("Requested file could not be found.");
}
在调用代码中捕获错误:
try {my_method();
} catch (IOError e) {stdout.printf("Error: %s\n", e.message);
}
使用 "is" 运算符比较错误代码:
IOChannel channel;
try {channel = new IOChannel.file("/tmp/my_lock", "w");
} catch (FileError e) {if(e is FileError.EXIST) {throw e;}GLib.error("", e.message);
}
所有这些看起来或多或少与其他语言类似,但定义允许的错误类型是相当独特的。错误有三个组成部分,称为"域"、"代码"和"消息"。消息我们已经见过,它只是在创建错误时提供的一段文本。错误域描述问题的类型,相当于 Java 中的 "Exception" 子类或类似的东西。在上面的例子中,我们设想了一个名为 "IOError" 的错误域。第三部分,错误代码,是描述遇到的具体问题类型的细化。每个错误域有一个或多个错误代码——在例子中有一个名为 "FILE_NOT_FOUND" 的代码。
定义这种关于错误类型信息的方式与 glib 中的实现有关。为了使这里的例子工作,需要如下定义:
errordomain IOError {FILE_NOT_FOUND
}
当捕获错误时,你指定希望捕获的错误域,如果该域中的错误被抛出,处理程序中的代码就会运行,并将错误赋值给提供的名称。从该错误对象中,你可以根据需要提取错误代码和消息。如果你想捕获来自多个域的错误,只需提供额外的 catch 块。还有一个可选的块,可以放在 try 和任何 catch 块之后,称为 finally。无论是否抛出错误或是否执行了任何 catch 块,甚至如果错误实际上未被处理并将再次抛出,这段代码都会在该部分末尾始终运行。例如,这允许释放在 try 块中保留的任何资源,无论引发了任何错误。这些特性的完整示例:
public errordomain ErrorType1 {CODE_1A
}public errordomain ErrorType2 {CODE_2A,CODE_2B
}public class Test : GLib.Object {public static void thrower() throws ErrorType1, ErrorType2 {throw new ErrorType1.CODE_1A("Error");}public static void catcher() throws ErrorType2 {try {thrower();} catch (ErrorType1 e) {// 处理 ErrorType1} finally {// 清理工作}}public static int main(string[] args) {try {catcher();} catch (ErrorType2 e) {// 处理 ErrorType2if (e is ErrorType2.CODE_2B) {// 处理此代码}}return 0;}
}
这个例子有两个错误域,两者都可以被 "thrower" 方法抛出。Catcher 只能抛出第二种类型的错误,因此如果 "thrower" 抛出第一种类型,它必须处理。最后,"main" 方法将处理来自 "catcher" 的任何错误。