文章目录
- 第二章 Caché 命令大全 CATCH 命令
- 重点
- 大纲
- 参数
- 描述
- 捕获异常处理
- 嵌套的`TRY/CATCH`块
- 执行堆栈
- `CATCH`和`$ZTRAP`,`$ETRAP`
- `TRY / CATCH `循环
- 禁用`CATCH`
- 参数
- exceptionvar
- 示例:系统异常
- 示例:抛出的异常
- 示例:嵌套的`TRY/CATCH`
第二章 Caché 命令大全 CATCH 命令
标识发生异常时要执行的代码块。
重点
- 不能使用带参数的
QUIT
退出CATCH
块.全完退出用RETURN
。 - 不推荐使用无参数CATCH。
- 自定义异常与ISA()判断异常类型。
大纲
CATCH exceptionvar
{
. . .
}
参数
- exceptionvar 可选-异常变量。指定为局部变量,带或不带下标,接收对Caché Object (OREF)的引用。可以选择使用括号将此参数括起来。
描述
CATCH
命令定义了一个异常处理程序,一个在TRY
代码块中发生异常时要执行的代码块。Catch
命令后面跟有一段代码语句,用大括号括起来。
如果指定TRY
块,则需要CATCH
块;每个TRY
块必须具有对应的CATCH
块。每个TRY
块只允许一个CATCH
块。CATCH
块必须紧跟在其TRY
块之后。TRY
块和其CATCH
块之间不允许有可执行代码行。TRY
块与其CATCH
块之间或与CATCH
命令相同的行上不允许有任何标签。但是,可以在TRY
块和其CATCH
块之间包含注释。
当发生异常时,将输入CATCH
块。如果未发生异常,则不应执行CATCH
块。切勿使用GOTO
语句进入CATCH
块。
可以使用QUIT
或RETURN
退出CATCH
块。QUIT
退出当前块结构,并继续执行该块结构之外的下一个命令。例如,如果在嵌套的CATCH
块中,则发出QUIT
命令会将该CATCH
块退出到封闭的块结构。不能使用带参数的QUIT
退出CATCH
块;尝试这样做会导致编译错误。 若要从CATCH
块内完全退出例程,请发出RETURN
语句。
CATCH
命令有两种形式:
- 无参数
- 有参
捕获异常处理
CATCH exceptionvar从try
块接收对象实例引用(OREF),在发生系统错误的情况下,该引用可以由Throw
命令显式传递,也可以隐式从系统运行时环境传递。此对象提供包含有关异常的信息的属性。
一个异常可以传递四个要捕获的异常属性。它们按顺序是:名称、代码、位置和数据。引发的异常不能传递位置参数。可以使用%ISA()
实例方法来确定在这些属性中传递的异常类型。
在下面的示例中,try
块可以生成系统异常(未定义的局部变量)、引发SQL异常、引发状态异常或引发常规异常。此通用捕获异常处理程序确定发生哪种类型的异常,并显示相应的属性。它显示系统异常的所有四个属性(Data属性是某些类型的系统错误的空字符串)。它显示SQL异常的两个属性(代码和数据)。它向$SYSTEM.Status.Error()
提供两个属性,以生成状态异常的错误消息字符串。它显示常规ObjectScript异常的三个属性(名称、代码和数据)。它使用$ZCVT
函数格式化包含尖括号的名称值以供浏览器显示:
/// d ##class(PHA.TEST.Command).TestCatch()
ClassMethod TestCatch()
{
TRY {
SET x=$RANDOM(4)
IF x=0 {
KILL undefvar
WRITE undefvar
}
ELSEIF x=1 {
SET oref=##class(%Exception.SQL).%New(,"-999",,"SQL error message")
THROW oref
}
ELSEIF x=2 {
SET oref=##class(%Exception.StatusException).%New(,"5002",,$LISTBUILD("My Status Error"))
THROW oref
}
ELSE {
SET oref=##class(%Exception.General).%New("<MY BAD>","999",,"General error message")
THROW oref
}
WRITE "this should not display",!
}
CATCH exp {
WRITE "In the CATCH block",!
IF 1=exp.%IsA("%Exception.SystemException") {
WRITE "System exception",!
WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
WRITE "Location: ",exp.Location,!
WRITE "Code: "
}
ELSEIF 1=exp.%IsA("%Exception.SQL") {
WRITE "SQL exception",!
WRITE "SQLCODE: "
}
ELSEIF 1=exp.%IsA("%Exception.StatusException") {
WRITE "Status exception",!
DO $SYSTEM.Status.DisplayError($SYSTEM.Status.Error(exp.Code,exp.Data))
RETURN
}
ELSEIF 1=exp.%IsA("%Exception.General") {
WRITE "General ObjectScript exception",!
WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
WRITE "Code: "
}
ELSE {
WRITE "Some other type of exception",! RETURN }
WRITE exp.Code,!
WRITE "Data: ",exp.Data,!
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatch()
In the CATCH block
General ObjectScript exception
Name: <MY BAD>
Code: 999
Data: General error message
DHC-APP>d ##class(PHA.TEST.Command).TestCatch()
In the CATCH block
Status exception
错误 #5002: Cache错误: My Status Error
DHC-APP>d ##class(PHA.TEST.Command).TestCatch()
In the CATCH block
SQL exception
SQLCODE: -999
Data: SQL error message
DHC-APP>d ##class(PHA.TEST.Command).TestCatch()
In the CATCH block
System exception
Name: <UNDEFINED>
Location: zTestCatch+4^PHA.TEST.Command.1
Code: 9
Data: undefvar
嵌套的TRY/CATCH
块
每个TRY
块只允许一个CATCH
块。但是,可以嵌套成对的TRY/CATCH
块。
可以将内部TRY/CATCH
对嵌套在外部CATCH
块中,如下所示:
TRY {
}
CATCH exvar1 {
TRY {
}
CATCH exvar2 {
}
}
可以将内部TRY/CATCH
对嵌套在外部TRY
块中,如下所示:
TRY {
TRY {
}
CATCH exvar2 {
}
}
CATCH exvar1 {
}
执行堆栈
%Exception
对象包含创建该对象时的执行堆栈。可以使用StackAsArray()
方法访问此执行堆栈。下面的示例显示此执行堆栈:
/// d ##class(PHA.TEST.Command).TestCatchStack()
ClassMethod TestCatchStack()
{
TRY {
WRITE "In the TRY block",!
WRITE 7/0
}
CATCH exobj {
WRITE "In the CATCH block",!
WRITE $ZCVT($ZERROR,"O","HTML"),!
TRY {
WRITE "In the nested TRY block",!
KILL fred
WRITE fred
}
CATCH exobj2 {
WRITE "In the nested CATCH block",!
WRITE $ZCVT($ZERROR,"O","HTML"),!!
WRITE "The Execution Stack",!
DO exobj2.StackAsArray(.stk)
ZWRITE stk
}
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchStack()
In the TRY block
In the CATCH block
<DIVIDE>zTestCatchStack+3^PHA.TEST.Command.1
In the nested TRY block
In the nested CATCH block
<UNDEFINED>zTestCatchStack+11^PHA.TEST.Command.1 *fred
The Execution Stack
stk=2
stk(1)="DO"
stk(1,"PLACE")=" 0"
stk(2)=""
stk(2,"PLACE")="zTestCatchStack+11^PHA.TEST.Command.1 1"
CATCH
和$ZTRAP
,$ETRAP
不能在TRY
块内设置$ZTRAP
或$ETRAP
。但是,可以在CATCH
块中设置$ZTRAP
或$ETRAP
。还可以在进入TRY
块之前设置$ZTRAP
或$ETRAP
。如果CATCH
块内发生异常,则采用指定的$ZTRAP
或$ETRAP
异常处理程序。
TRY / CATCH
循环
TRY
块调用返回到TRY
块的CATCH
块的循环不会无限循环。它最终会发出<FRAMESTACK>
错误。
禁用CATCH
发出ZBREAK /ERRORTRAP:OFF
命令将禁用捕获异常处理。
参数
exceptionvar
-
局部变量,用于在发生系统错误时从抛出命令或从系统运行时环境接收异常对象引用。
-
发生系统错误时,exceptionvar接收对
%Exception.SystemException
类型的对象的引用。 -
当发生用户指定的错误时,exceptionvar接收对
%Exception.General、%Exception.StatusException或%Exception.SQL
类型的对象的引用。 -
可以选择使用括号将exceptionvar参数括起来,例如:
Catch(Var){code block}
。提供此括号语法是为了兼容,对功能没有影响。
示例:系统异常
下面的示例显示由被零除的运行时错误调用的无参数Catch
。它显示$ZERROR
和$ECODE
错误值。不推荐使用无参数CATCH
,因为它不如传递exceptionvar可靠。如果CATCH
块中发生错误,$ZERROR
将包含此最新错误,而不是调用CATCH
的错误。 在本例中,QUIT
命令退出CATCH
块,但不阻止“fall-through”
到块结构之外的下一行:
/// d ##class(PHA.TEST.Command).TestCatchZERROR()
ClassMethod TestCatchZERROR()
{
TRY {
WRITE !,"Try块即将被零除",!!
SET a=7/0
WRITE !,"这不应显示"
}
CATCH {
WRITE "Catch块异常处理程序",!!
WRITE "$ZERROR is: ",$ZERROR,!
WRITE "$ECODE is :",$ECODE,!
QUIT
WRITE !,"这不应显示"
}
WRITE !,"这就是代码失败的地方falls through"
}
DHC-APP> d ##class(PHA.TEST.Command).TestCatchZERROR()
Try块即将被零除
Catch块异常处理程序
$ZERROR is: <DIVIDE>zTestCatchZERROR+3^PHA.TEST.Command.1
$ECODE is :,M6,M17,M6,M9,M6,M9,M6,M9,M9,
这就是代码失败的地方falls through
下面的示例显示由被零除的运行时错误调用并接收参数的Catch
。这是首选的编码方式。myexp
OREF参数接收系统生成的异常对象。它显示此异常实例的名称、代码和位置属性。在此示例中,RETURN命令退出程序,因此不会发生“fall-through”
:
/// d ##class(PHA.TEST.Command).TestCatchNOZERROR()
ClassMethod TestCatchNOZERROR()
{
TRY {
WRITE !,"Try块即将被零除",!!
SET a=7/0
WRITE !,"这不应显示"
}
CATCH myexp {
WRITE "Catch块异常处理程序",!!
WRITE "Name: ",$ZCVT(myexp.Name,"O","HTML"),!
WRITE "Code: ",myexp.Code,!
WRITE "Location: ",myexp.Location,!
RETURN
}
WRITE !,"这就是代码失败的地方 falls through"
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchNOZERROR()
Try块即将被零除
Catch块异常处理程序
Name: <DIVIDE>
Code: 18
Location: zTestCatchNOZERROR+3^PHA.TEST.Command.1
下面的示例显示了一个接收系统异常对象的CATCH
。CATCH块
代码使用%Exception.SystemException
类的AsSystemError()
方法将系统异常显示为$ZERROR
格式的字符串。(为了便于比较,还会显示$ZERROR
。)然后,此CATCH
块将错误名称、错误代码、错误数据和错误位置显示为单独的属性:
/// d ##class(PHA.TEST.Command).TestCatchException()
ClassMethod TestCatchException()
{
TRY {
WRITE !,"此global未定义",!
SET a = ^badglobal(1)
WRITE !,"这不应显示"
}
CATCH myvar {
WRITE !,"这是异常处理程序",!
WRITE "AsSystemError is: ",myvar.AsSystemError(),!
WRITE "$ZERROR is: ",$ZERROR,!!
WRITE "Error name=",$ZCVT(myvar.Name,"O","HTML"),!
WRITE "Error code=",myvar.Code,!
WRITE "Error data=",myvar.Data,!
WRITE "Error location=",myvar.Location,!
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchException()
此global未定义
这是异常处理程序
AsSystemError is: <UNDEFINED>zTestCatchException+3^PHA.TEST.Command.1 ^badglobal(1)
$ZERROR is: <UNDEFINED>zTestCatchException+3^PHA.TEST.Command.1 ^badglobal(1)
Error name=<UNDEFINED>
Error code=93
Error data=^badglobal(1)
Error location=zTestCatchException+3^PHA.TEST.Command.1
示例:抛出的异常
下面的示例显示了由Throw
命令调用的Catch
。myvar
参数接收具有四个属性的用户定义的异常对象。请注意,在此示例中,抛出不为%Exception.General
类的省略Location属性提供值:
/// d ##class(PHA.TEST.Command).TestCatchThrow()
ClassMethod TestCatchThrow()
{
TRY {
SET total=1234
WRITE !,"Throw an exception引发异常!"
THROW ##class(%Exception.General).%New("Example Error",999,,"MyThrow")
WRITE !,"这不应显示"
}
CATCH myvar {
WRITE !!,"这是异常处理程序"
WRITE !,"Error data=",myvar.Data
WRITE !,"Error code=",myvar.Code
WRITE !,"Error name=",myvar.Name
WRITE !,"Error location=",myvar.Location,!
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchThrow()
Throw an exception引发异常!
这是异常处理程序
Error data=MyThrow
Error code=999
Error name=Example Error
Error location=
以下两个示例在try
块中生成出生日期。如果它们生成将来的出生日期,则使用Throw
发出一般异常,并将用户定义的异常传递给Catch
块。(可能需要多次运行这些示例才能生成抛出异常的日期。)
这些示例中的第一个没有指定Catch
exceptionvar。它使用TRY
块中定义的OREF名称指定异常属性:
/// d ##class(PHA.TEST.Command).TestCatchThrowFirst()
ClassMethod TestCatchThrowFirst()
{
TRY {
WRITE "在TRY块中",!
SET badDOB=##class(%Exception.General).%New("BadDOB","999",,"Birth date is in the future")
FOR x=1:1:20 {
SET rndDOB = $RANDOM(7)_$RANDOM(10000)
IF rndDOB > $HOROLOG {
THROW badDOB
}
ELSE {
WRITE "Birthdate ",$ZDATE(rndDOB,1,,4)," is valid",!
}
}
}
CATCH {
WRITE !,"在CATCH块中"
WRITE !,"Birthdate ",$ZDATE(rndDOB,1,,4)," is invalid"
WRITE !,"Error code=",badDOB.Code
WRITE !,"Error name=",badDOB.Name
WRITE !,"Error data=",badDOB.Data
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchThrowFirst()
在TRY块中
Birthdate 01/15/1877 is valid
Birthdate 01/12/1998 is valid
Birthdate 02/08/1958 is valid
在CATCH块中
Birthdate 12/12/2023 is invalid
Error code=999
Error name=BadDOB
Error data=Birth date is in the future
这些示例中的第二个指定了Catch exceptionvar。它使用这个重命名的OREF来指定异常属性。这是首选用法:
/// d ##class(PHA.TEST.Command).TestCatchThrowSecond()
ClassMethod TestCatchThrowSecond()
{
TRY {
WRITE "在TRY块中",!
SET badDOB=##class(%Exception.General).%New("BadDOB","999",,"Birth date is in the future")
FOR x=1:1:20 {
SET rndDOB = $RANDOM(7)_$RANDOM(10000)
IF rndDOB > $HOROLOG {
THROW badDOB
}
ELSE {
WRITE "Birthdate ",$ZDATE(rndDOB,1,,4)," is valid",!
}
}
}
CATCH err {
WRITE !,"在CATCH块中"
WRITE !,"Birthdate ",$ZDATE(rndDOB,1,,4)," is invalid"
WRITE !,"Error code=",err.Code
WRITE !,"Error name=",err.Name
WRITE !,"Error data=",err.Data
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchThrowSecond()
在TRY块中
Birthdate 03/15/2012 is valid
在CATCH块中
Birthdate 05/20/2032 is invalid
Error code=999
Error name=BadDOB
Error data=Birth date is in the future
示例:嵌套的TRY/CATCH
下面的示例显示由被零除的运行时错误调用的Catch
。CATCH
块包含与内部CATCH
块成对的内部TRY
块。此内部CATCH
块由引发的异常调用。出于演示的目的,在此程序中随机调用此THROW
。在实际程序中,内部CATCH
块将由异常测试调用,例如AsSystemError()
(捕获的错误)和$ZERROR
(最新错误)之间的不匹配:
/// d ##class(PHA.TEST.Command).TestCatchThrowNested()
ClassMethod TestCatchThrowNested()
{
TRY {
WRITE !,"外部TRY块",!!
SET a=7/0
WRITE !,"这不应显示"
}
CATCH myexp {
WRITE "外部CATCH块",!
WRITE "Name: ",$ZCVT(myexp.Name,"O","HTML"),!
WRITE "Code: ",myexp.Code,!
WRITE "Location: ",myexp.Location,!
SET rndm=$RANDOM(2)
IF rndm=1 {
RETURN
}
TRY {
WRITE !,"内部TRY块",!
SET oref=##class(%Exception.General).%New("<MY BAD>","999",,"一般错误消息")
THROW oref
RETURN
}
CATCH myexp2 {
WRITE !,"内部CATCH块",!
IF 1=myexp2.%IsA("%Exception.General") {
WRITE "常规ObjectScript异常",!
WRITE "Name: ",$ZCVT(myexp2.Name,"O","HTML"),!
WRITE "Code: ",myexp2.Code,!
}
ELSE {
WRITE "一些其他类型的异常",!
}
QUIT
}
WRITE !,"返回到外部CATCH块",!
RETURN
}
}
DHC-APP>d ##class(PHA.TEST.Command).TestCatchThrowNested()
外部TRY块
外部CATCH块
Name: <DIVIDE>
Code: 18
Location: zTestCatchThrowNested+3^PHA.TEST.Command.1
内部TRY块
内部CATCH块
常规ObjectScript异常
Name: <MY BAD>
Code: 999
返回到外部CATCH块