第二十七章 Caché 命令大全 TRY 命令

第二十七章 Caché 命令大全 TRY 命令

标识要在执行期间监视错误的代码块。

重点

  1. 不能使用带参数的QUIT退出TRY块;尝试这样做会导致编译错误。要完全从TRY块中退出例程,请发出RETURN语句。
  2. 不能在TRY块中设置$ ZTRAP$ ETRAP
  3. 如果在进入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

可以使用QUITRETURN退出TRY块。 QUIT退出当前块结构,并使用该块结构之外的下一个命令继续执行。例如,如果在嵌套的TRY块中,则发出QUIT可使该TRY块退出到封闭的块结构中。在TRY块中发出QUIT命令会将执行转移到相应CATCH块之后的第一条代码行。不能使用带参数的QUIT退出TRY块;尝试这样做会导致编译错误。要完全从TRY块中退出例程,请发出RETURN语句。

在极少数情况下,TRYQUITRETURN命令可能会生成异常。如果TRY创建了新上下文,然后删除了旧上下文的某些方面,则可能会发生这种情况;尝试还原到旧上下文将导致异常。TRYQUITRETURN异常不会调用CATCH块异常处理程序。

ETRAP

TRYCATCH命令在执行级别内执行错误处理。当TRY块中发生异常时,Caché通常会执行紧随TRY块之后的异常处理程序代码的CATCH块。这是首选的错误处理行为。

不能在TRY块中设置$ ZTRAP$ ETRAP。但是,可以在TRY块内的无参数DO块内设置$ ZTRAP$ ETRAP。这是因为DO块的行处于其自己的执行级别,因此可以具有自己的错误处理程序。

如果在进入TRY块之前设置了$ ZTRAP,并且TRY块内发生异常,则Caché将使用CATCH块而不是$ ZTRAP

如果在进入TRY块之前设置了$ ETRAP,并且在TRY块内发生了异常,则Caché可能会采用$ ETRAP而不是CATCH,除非阻止了这种可能性。如果发生异常时$ ETRAPCATCH同时存在,则Caché将执行适用于当前执行级别的错误代码(CATCH$ ETRAP)。因为$ ETRAP本质上不与执行级别相关联,所以除非另外指定,否则Caché假定它与当前执行级别相关联。在设置$ ETRAP之前,必须先新建$ ETRAP才能为$ ETRAP建立级别标记,以便Caché可以正确地将CATCH用作当前级别的异常处理程序,而不是$ ETRAP。否则,系统错误(包括THROW命令引发的系统错误)可能会占用$ ETRAP异常处理程序。

GOTO 与 DO

可以使用GOTODO命令在TRY块内的标签处输入TRY块。如果稍后在TRY块中发生异常,则将采用CATCH块异常处理程序,就像在TRY关键字中输入TRY块一样。但是,为使编码清晰起见,应避免使用GOTODO进入TRY块。

当然,可以从TRY块或CATCH块中发出GOTO

强烈建议不要使用GOTODO输入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: zTestTry1+5^PHA.TEST.Command.1 *x
$ECODE: ,M7,M6,

TRY退出。在以下示例中,由于TRY块的执行以QUITRETURN(不是错误)结束,因此未执行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: zTestTry1+5^PHA.TEST.Command.1 *x
$ECODE: ,M7,M6,
DHC-APP>d ##class(PHA.TEST.Command).TestTry2()
第一个TRY块
x是一个未定义的变量
发出 RETURN

你可能感兴趣的:(第二十七章 Caché 命令大全 TRY 命令)