第二十七章 Caché 命令大全 TRY 命令
标识要在执行期间监视错误的代码块。
重点
- 不能使用带参数的
QUIT退出TRY块;尝试这样做会导致编译错误。要完全从TRY块中退出例程,请发出RETURN语句。 - 不能在
TRY块中设置$ ZTRAP或$ ETRAP。 - 如果在进入
TRY块之前设置了$ ZTRAP,并且TRY块内发生异常,则Caché将使用CATCH块而不是$ ZTRAP。
大纲
TRY {
. . .
}
描述
Try命令不带任何参数。它用于标识用大括号括起来的CachéObjectScript代码语句块。此代码块是用于结构化异常处理的受保护代码。如果此代码块内发生异常,则Caché设置$ZERROR和$ECODE,然后将执行转移到由Catch命令标识的异常处理程序。
异常可能由于运行时错误(如试图除以0)而发生,也可能通过发出Throw命令显式传播。如果没有发生错误,则继续执行Catch代码块之后的下一条ObjectScript语句。
TRY块后面必须紧跟CATCH块。不能在try代码块的右花括号和catch命令之间指定可执行代码语句或标签。但是,可以在TRY和块及其CATCH块之间指定注释。每个TRY块只允许一个CATCH块。但是,可以嵌套成对的TRY / CATCH块,例如:
TRY {
/* TRY code */
TRY {
/* nested TRY code */
}
CATCH {
/* nested CATCH code */
}
}
CATCH {
/* CATCH code */
}
通常,一个CachéObjectScript程序由多个TRY块组成,每个TRY块紧随其后的是与其相关的CATCH块。
QUIT 与 RETURN
可以使用QUIT或RETURN退出TRY块。 QUIT退出当前块结构,并使用该块结构之外的下一个命令继续执行。例如,如果在嵌套的TRY块中,则发出QUIT可使该TRY块退出到封闭的块结构中。在TRY块中发出QUIT命令会将执行转移到相应CATCH块之后的第一条代码行。不能使用带参数的QUIT退出TRY块;尝试这样做会导致编译错误。要完全从TRY块中退出例程,请发出RETURN语句。
在极少数情况下,TRY块QUIT或RETURN命令可能会生成异常。如果TRY创建了新上下文,然后删除了旧上下文的某些方面,则可能会发生这种情况;尝试还原到旧上下文将导致异常。TRY块QUIT或RETURN异常不会调用CATCH块异常处理程序。
ETRAP
TRY和CATCH命令在执行级别内执行错误处理。当TRY块中发生异常时,Caché通常会执行紧随TRY块之后的异常处理程序代码的CATCH块。这是首选的错误处理行为。
不能在TRY块中设置$ ZTRAP或$ ETRAP。但是,可以在TRY块内的无参数DO块内设置$ ZTRAP或$ ETRAP。这是因为DO块的行处于其自己的执行级别,因此可以具有自己的错误处理程序。
如果在进入TRY块之前设置了$ ZTRAP,并且TRY块内发生异常,则Caché将使用CATCH块而不是$ ZTRAP。
如果在进入TRY块之前设置了$ ETRAP,并且在TRY块内发生了异常,则Caché可能会采用$ ETRAP而不是CATCH,除非阻止了这种可能性。如果发生异常时$ ETRAP和CATCH同时存在,则Caché将执行适用于当前执行级别的错误代码(CATCH或$ ETRAP)。因为$ ETRAP本质上不与执行级别相关联,所以除非另外指定,否则Caché假定它与当前执行级别相关联。在设置$ ETRAP之前,必须先新建$ ETRAP才能为$ ETRAP建立级别标记,以便Caché可以正确地将CATCH用作当前级别的异常处理程序,而不是$ ETRAP。否则,系统错误(包括THROW命令引发的系统错误)可能会占用$ ETRAP异常处理程序。
GOTO 与 DO
可以使用GOTO或DO命令在TRY块内的标签处输入TRY块。如果稍后在TRY块中发生异常,则将采用CATCH块异常处理程序,就像在TRY关键字中输入TRY块一样。但是,为使编码清晰起见,应避免使用GOTO或DO进入TRY块。
当然,可以从TRY块或CATCH块中发出GOTO。
强烈建议不要使用GOTO或DO输入CATCH块。
在TRY区块内执行DO
当使用TRY语句时,THROW导致试图查找适当的CATCH块的帧堆栈搜索。当帧堆栈指示在TRY块内执行时,将在相应的CATCH块处继续执行。但是,Caché必须在执行CATCH块之前删除当前TRY块内的所有“本地”调用。
如果TRY块包含导致重新输入该TRY块的DO语句,则可能会发生以下两种情况之一:
“本地” DO调用(保留在当前TRY块内的DO调用):如果先前的帧堆栈条目是位于同一TRY块中的DO调用,则该DO被假定为当前内部的“本地”子例程调用尝试块。在这种情况下,不会立即输入CATCH,而是弹出框架堆栈(可能会删除一些最近分配的NEW变量),并在当前TRY块中的DO调用处继续搜索。如果新的先前帧堆栈条目不是当前TRY块内部的DO,则输入相应的CATCH块。但是,如果先前的帧堆栈条目是同一TRY中的另一个DO,则再次弹出该帧堆栈(以及最近分配的NEW变量)。继续此操作,直到上一个帧堆栈条目不是DO,然后才进入CATCH块。
“递归” DO调用(在TRY块内的DO调用离开TRY块,但稍后执行重新进入该TRY块):搜索CATCH块时,如果先前的帧堆栈条目是当前TRY块内的DO,如果该先前堆栈帧的目标标签不在当前TRY块(包括任何嵌套的TRY块)内,则不会弹出该帧堆栈(并且不会弹出最近分配的局部变量),并且立即输入CATCH块。请注意,如果该CATCH块又进行了一次THROW,则可能会重新输入当前的CATCH块,因为递归DO帧仍在帧堆栈上。
示例
本节中的示例显示运行时错误(%Exception.SystemException错误)。 DO框架仍在框架堆栈上。
在以下示例中,将执行TRY代码块。它尝试设置局部变量a。在第一个示例中,代码成功完成,并且跳过了CATCH。在第二个示例中,代码失败并显示错误,并且执行被传递给CATCH异常处理程序。
TRY成功。 CATCH块被跳过。执行从第二个TRY块继续:
/// d ##class(PHA.TEST.Command).TestTry()
ClassMethod TestTry()
{
TRY {
WRITE "第一个TRY块",!
SET x="fred"
WRITE "x是定义的变量",!
SET a=x
}
CATCH exp
{
WRITE !,"这是CATCH异常处理程序",!
IF 1=exp.%IsA("%Exception.SystemException") {
WRITE "系统异常",!
WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
WRITE "Location: ",exp.Location,!
WRITE "Code: ",exp.Code,!
WRITE "Data: ",exp.Data,!!
} ELSE {
WRITE "不是系统异常",!!
}
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
RETURN
}
TRY {
WRITE !,"第二个TRY块",!
WRITE "这是代码失败的地方",!
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
}
CATCH exp2 {
WRITE !,"这是第二个CATCH异常处理程序",!
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestTry()
第一个TRY块
x是定义的变量
第二个TRY块
这是代码失败的地方
$ZERROR:
$ECODE:
TRY失败。执行将继续执行CATCH块。 CATCH块以RETURN结尾,因此不执行第二个TRY块:
/// d ##class(PHA.TEST.Command).TestTry1()
ClassMethod TestTry1()
{
TRY {
WRITE "第一个TRY块",!
KILL x
WRITE "x是一个未定义的变量",!
SET a=x
}
CATCH exp
{
WRITE !,"这是CATCH异常处理程序",!
IF 1=exp.%IsA("%Exception.SystemException") {
WRITE "系统异常",!
WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
WRITE "Location: ",exp.Location,!
WRITE "Code: ",exp.Code,!
WRITE "Data: ",exp.Data,!!
} ELSE {
WRITE "不是系统异常",!!
}
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
RETURN
}
TRY {
WRITE !,"第二个TRY块",!
WRITE "这是代码失败的地方",!
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
}
CATCH exp2 {
WRITE !,"这是第二个CATCH异常处理程序",!
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestTry1()
第一个TRY块
x是一个未定义的变量
这是CATCH异常处理程序
系统异常
Name: <UNDEFINED>
Location: zTestTry1+5^PHA.TEST.Command.1
Code: 9
Data: x
$ZERROR: <UNDEFINED>zTestTry1+5^PHA.TEST.Command.1 *x
$ECODE: ,M7,M6,
TRY退出。在以下示例中,由于TRY块的执行以QUIT或RETURN(不是错误)结束,因此未执行CATCH块。如果RETURN,则程序执行停止。如果为QUIT,程序将继续执行第二个TRY块:
/// d ##class(PHA.TEST.Command).TestTry2()
ClassMethod TestTry2()
{
TRY {
WRITE "第一个TRY块",!
KILL x
WRITE "x是一个未定义的变量",!
SET decide=$RANDOM(2)
IF decide=0 {
WRITE "发出 QUIT",!
QUIT
}
IF decide=1 {
WRITE "发出 RETURN",!
RETURN
}
WRITE "这不应该显示",!
SET a=x
}
CATCH exp {
WRITE !,"这是CATCH异常处理程序",!
IF 1=exp.%IsA("%Exception.SystemException") {
WRITE "System exception",!
WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
WRITE "Location: ",exp.Location,!
WRITE "Code: ",exp.Code,!
WRITE "Data: ",exp.Data,!!
} ELSE {
WRITE "不是系统异常",!!
}
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
RETURN
}
TRY {
WRITE !,"第二个TRY块",!
WRITE "这是代码失败的地方",!
WRITE "$ZERROR: ",$ZERROR,!
WRITE "$ECODE: ",$ECODE
}
CATCH exp2 {
WRITE !,"这是第二个CATCH异常处理程序",!
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestTry2()
第一个TRY块
x是一个未定义的变量
发出 QUIT
第二个TRY块
这是代码失败的地方
$ZERROR: <UNDEFINED>zTestTry1+5^PHA.TEST.Command.1 *x
$ECODE: ,M7,M6,
DHC-APP>d ##class(PHA.TEST.Command).TestTry2()
第一个TRY块
x是一个未定义的变量
发出 RETURN