CATCH (ObjectScript)
構文
CATCH exceptionvar
{
. . .
}
引数
引数 | 説明 |
---|---|
exceptionvar | オプション — 例外変数。InterSystems IRIS オブジェクトへの参照 (OREF) を受け取るローカル変数として指定され、添え字がある場合とない場合があります。オプションで、この引数を括弧で囲むことができます。 |
説明
CATCH コマンドは、例外ハンドラを定義します。これは、TRY コード・ブロックで例外が発生したときに実行されるコードのブロックです。CATCH コマンドの後には、中括弧 ( { } ) で囲まれたコード文のブロックが続きます。
TRY ブロックを指定する場合、CATCH ブロックが必須であり、TRY ブロックごとに対応する CATCH ブロックを含める必要があります。CATCH ブロックは 1 つのみが、各 TRY ブロックに対して許可されます。TRY ブロックのすぐ後に、CATCH ブロックが続く必要があります。TRY ブロックとその CATCH ブロックの間に実行可能コード行を含めることはできません。TRY ブロックとその CATCH ブロックの間、および CATCH コマンドと同じ行にラベルを含めることはできません。ただし、TRY ブロックとその CATCH ブロックの間にコメントを含めることはできます。
CATCH ブロックは、例外が発生したときに開始されます。例外が発生しない場合は、CATCH ブロックを実行しないでください。絶対に GOTO 文を使用して CATCH ブロックに入らないでください。
CATCH ブロックは、QUIT または RETURN で終了できます。QUIT は、現在のブロック構造を終了し、ブロック構造の外側にある次のコマンドで実行を続けます。例えば、入れ子の CATCH ブロック内で QUIT を発行すると、CATCH ブロックを終了して囲んでいるブロック構造に移動できます。引数付きの QUIT を使用して、CATCH ブロックを終了することはできません。これを行うと、コンパイル・エラーが返されます。CATCH ブロック内から完全にルーチンを終了するには、RETURN 文を発行します。
CATCH コマンドには、以下の 2 つの基本形式があります。
-
引数なし
-
引数付き
引数付きの形式が推奨されます。
CATCH 例外処理
CATCH exceptionvar は、TRY ブロックからオブジェクト・インスタンス参照 (OREF) を受け取ります。これは、THROW コマンドによって明示的に渡されるか、システム・エラー発生時にシステムの実行環境から暗黙的に渡されます。OREFの詳細は、"OREF の基本" を参照してください。
このオブジェクト・インスタンス参照は、例外に関する情報を含むプロパティを提供します。
例外により、4 つの例外プロパティが CATCH に渡されます。これらのプロパティは、順番に、Name、Code、Location、および Data です。スローされた例外の場合、Location パラメータは渡されません。%IsA() インスタンス・メソッドを使用することで、これらのプロパティで渡された例外の種類を判別できます。
以下の例において、TRY ブロックは、システム例外 (未定義のローカル変数) を生成したり、SQL 例外をスローしたり、%Status 例外をスローしたり、一般例外をスローしたりする場合があります。この汎用的な CATCH 例外ハンドラは、どのような例外が発生したかを判別して、適切なプロパティを表示します。システム例外に関する 4 つのプロパティをすべて表示します (Data プロパティは、一部の種類のシステム・エラーについて空の文字列になります)。SQL 例外に関する 2 つのプロパティ (Code および Data) を表示します。 $SYSTEM.Status.Error() に対して 2 つのプロパティを提供して、%Status 例外に関するエラー・メッセージの文字列を生成します。一般的な ObjectScript 例外に関する 3 つのプロパティ (Name、Code、および Data) を表示します。$ZCVT 関数を使用して、ブラウザの表示用に角括弧を含む Name 値を形式設定します。
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(exp.AsStatus())
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
}
入れ子になった TRY/CATCH ブロック
CATCH ブロックは 1 つのみが、各 TRY ブロックに対して許可されます。ただし、ペアになった TRY/CATCH ブロックを入れ子にすることはできます。
以下のように、外側の CATCH ブロック内に TRY/CATCH ペアを挿入して入れ子にすることができます。
TRY {
/* TRY code */
}
CATCH exvar1 {
/* CATCH code */
TRY {
/* nested TRY code */
}
CATCH exvar2 {
/* nested CATCH code */
}
}
以下のように、外側の TRY ブロック内に TRY/CATCH ペアを挿入して入れ子にすることができます。
TRY {
/* TRY code */
TRY {
/* nested TRY code */
}
CATCH exvar2 {
/* nested CATCH code */
}
}
CATCH exvar1 {
/* CATCH code */
}
実行スタック
%Exception オブジェクトには、オブジェクト作成時に実行スタックが含まれます。この実行スタックには、StackAsArray()Opens in a new tab メソッドを使用してアクセスできます。以下の例では、この実行スタックを示しています。
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
}
}
CATCH と $ZTRAP、$ETRAP
TRY ブロック内で $ZTRAP または $ETRAP を設定することはできません。ただし、CATCH ブロック内で $ZTRAP または $ETRAP を設定することはできます。また、TRY ブロックに入る前に $ZTRAP または $ETRAP を設定することもできます。
CATCH ブロック内で例外が発生した場合、指定された $ZTRAP または $ETRAP 例外ハンドラが実行されます。
TRY / CATCH ループ
TRY ブロックにループ・バックする CATCH ブロックを TRY ブロックが呼び出すループは無限ではありません。最終的には <FRAMESTACK> エラーが発行されます。
CATCH の無効化
ZBREAK /ERRORTRAP:OFF コマンドを実行すると CATCH 例外処理が無効になります。
引数
exceptionvar
システム・エラー発生時に、THROW コマンドまたはシステムの実行環境から例外オブジェクト参照を受け取るのに使用されるローカル変数。システム・エラーの発生時に、exceptionvar は %Exception.SystemExceptionOpens in a new tab 型のオブジェクトへの参照を受け取ります。ユーザ指定のエラーの発生時に、exceptionvar は %Exception.GeneralOpens in a new tab、%Exception.StatusExceptionOpens in a new tab、または %Exception.SQLOpens in a new tab 型のオブジェクトへの参照を受け取ります。詳細は、"インターシステムズ・クラス・リファレンス" の "%Exception.AbstractExceptionOpens in a new tab" 抽象クラスを参照してください。
オプションで、exceptionvar 引数を括弧で囲むことができます。したがって、CATCH(var) { code block } のようになります。この括弧構文は互換性の目的で提供されており、機能上の影響はありません。
例 : システム例外
以下は、0 による除算実行時エラーによって呼び出される引数なしの CATCH の例を示しています。これは、$ZERROR および $ECODE のエラー値を表示します。 引数なしの CATCH は、exceptionvar を渡すよりも信頼性が低いため、お勧めしません。CATCH ブロック内でエラーが発生した場合、$ZERROR には、CATCH を呼び出したエラーではなく、この最新のエラーが含まれます。この例では、QUIT コマンドは CATCH ブロックを終了しますが、ブロック構造の外側にある次の行への “フォールスルー” は妨げません。
TRY {
WRITE !,"TRY block about to divide by zero",!!
SET a=7/0
WRITE !,"this should not display"
}
CATCH {
WRITE "CATCH block exception handler",!!
WRITE "$ZERROR is: ",$ZERROR,!
WRITE "$ECODE is :",$ECODE,!
QUIT
WRITE !,"this should not display"
}
WRITE !,"this is where the code falls through"
以下は、0 による除算実行時エラーによって呼び出され、引数を受け取る CATCH の例を示しています。このコーディング方法をお勧めします。myexp OREF 引数は、システムで生成された例外オブジェクトを受け取ります。 これは、この例外インスタンスの Name、Code、および Location プロパティを表示します。この例では、RETURN コマンドはプログラムを終了するので、“フォールスルー” は発生しません。
TRY {
WRITE !,"TRY block about to divide by zero",!!
SET a=7/0
WRITE !,"this should not display"
}
CATCH myexp {
WRITE "CATCH block exception handler",!!
WRITE "Name: ",$ZCVT(myexp.Name,"O","HTML"),!
WRITE "Code: ",myexp.Code,!
WRITE "Location: ",myexp.Location,!
RETURN
}
WRITE !,"this is where the code falls through"
以下は、システム例外オブジェクトを受け取る CATCH の例を示しています。CATCH ブロック・コードは、%Exception.SystemExceptionOpens in a new tab クラスの AsSystemError()Opens in a new tab メソッドを使用して、システム例外を $ZERROR フォーマット済み文字列として表示します。($ZERROR も比較目的で表示されます。)その後、この CATCH ブロックは、エラー名、エラー・コード、エラー・データ、エラーの位置を個別のプロパティとして表示します。
TRY {
WRITE !,"this global is not defined",!
SET a=^badglobal(1)
WRITE !,"this should not display"
}
CATCH myvar {
WRITE !,"this is the exception handler",!
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
}
例 : スローされた例外
以下は、THROW コマンドによって呼び出される CATCH の例を示しています。myvar 引数は、4 つのプロパティを持つユーザ定義例外オブジェクトを受け取ります。この例では、THROW は省略された %Exception.GeneralOpens in a new tab クラスの Location プロパティの値を提供しないことに注意してください。
TRY {
SET total=1234
WRITE !,"Throw an exception!"
THROW ##class(%Exception.General).%New("Example Error",999,,"MyThrow")
WRITE !,"this should not display"
}
CATCH myvar {
WRITE !!,"this is the exception handler"
WRITE !,"Error data=",myvar.Data
WRITE !,"Error code=",myvar.Code
WRITE !,"Error name=",myvar.Name
WRITE !,"Error location=",myvar.Location,!
RETURN
}
以下の 2 つの例では、TRY ブロック内で誕生日を生成します。未来の誕生日を生成する場合、THROW を使用して一般例外を発行し、ユーザ定義例外を CATCH ブロックに渡します(例外をスローする日付を生成するためにこれらの例を複数回実行する必要がある場合があります)。
最初の例では、CATCH exceptionvar を指定しません。TRY ブロック内で定義された OREF 名を使用して、例外プロパティを指定します。
TRY {
WRITE "In the TRY block",!
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 !,"In the CATCH block"
WRITE !,"Birthdate ",$ZDATE(rndDOB,1,,4)," is invalid"
WRITE !,"Error code=",badDOB.Code
WRITE !,"Error name=",badDOB.Name
WRITE !,"Error data=",badDOB.Data
RETURN
}
2 番目の例では、CATCH exceptionvar を指定します。名前変更されたこの OREF を使用して、例外プロパティを指定します。これを使用することをお勧めします。
TRY {
WRITE "In the TRY block",!
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 !,"In the CATCH block"
WRITE !,"Birthdate ",$ZDATE(rndDOB,1,,4)," is invalid"
WRITE !,"Error code=",err.Code
WRITE !,"Error name=",err.Name
WRITE !,"Error data=",err.Data
RETURN
}
例 : 入れ子になった TRY/CATCH
以下は、0 による除算実行時エラーによって呼び出される CATCH の例を示しています。CATCH ブロックには、内側の CATCH ブロックとペアになる内側の TRY ブロックが含まれています。この内側の CATCH ブロックは、スローされた例外によって呼び出されます。デモンストレーションのために、この THROW は、このプログラム内ではランダムに呼び出されます。実際のプログラムでは、内側の CATCH ブロックは、AsSystemError() (捕捉エラー) と $ZERROR (最新エラー) 間の不一致などの例外テストによって呼び出されます。
TRY {
WRITE !,"Outer TRY block",!!
SET a=7/0
WRITE !,"this should not display"
}
CATCH myexp {
WRITE "Outer CATCH block",!
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 !,"Inner TRY block",!
SET oref=##class(%Exception.General).%New("<MY BAD>","999",,"General error message")
THROW oref
RETURN
}
CATCH myexp2 {
WRITE !,"Inner CATCH block",!
IF 1=myexp2.%IsA("%Exception.General") {
WRITE "General ObjectScript exception",!
WRITE "Name: ",$ZCVT(myexp2.Name,"O","HTML"),!
WRITE "Code: ",myexp2.Code,!
}
ELSE { WRITE "Some other type of exception",! }
QUIT
}
WRITE !,"back to Outer CATCH block",!
RETURN
}