最近、新しい throw()コマンドを使った新しいエラー処理メカニズムが導入されました。
これは、エラーを生成するコードに可能な限り近い場所に配置された、新しいエラー処理システムへの第一歩となりました。
4D 20 R4 では、式実行中のエラーをインターセプトできるように機能を拡張します。
その詳細を見ていきましょう。
try()、新しい便利なキーワード
4D 20 R4 の導入により、Try(Expression) という便利なキーワードが利用できるようになりました。Try() キーワードを使用すると、括弧で囲まれた式を実行して、その実行中にスローされたエラーをシームレスにインターセプトすることができます。これらのエラーは、式の実行直後に Last errorsコマンドで処理できます。これにより、エラーの発生源となるコードにできるだけ近いところでエラー処理を定義し、最終的にはローカル変数を使用することができます。
詳細な説明の前のサンプルシナリオ
ゼロによる除算
ゼロによる除算という簡単なシナリオを考えてみましょう。次のユークリッド除算メソッドのサンプルでは、除数がゼロの場合はエラーをスローし、戻り値としてゼロを返します。Try() を使用すると、エレガントにエラーを管理できます:
#DECLARE($dividend : Real; $divisor : Real)->$result : Real
If ($divisor=0)
$result:=0
throw(-12345; "ゼロによる除算!")
Else
$result:=($dividend/$divisor)
End if
$result:=Try(division($dividend; $divisor))
If (Last errors#Null)
// エラーへの対応
logErrors(Last errors)
End if
文書へのアクセス
ドキュメントにアクセスする際に、そのドキュメントが存在しない場合のエラー処理もより簡単になります。
4D のエラーダイアログが表示されないようにするには、エラー処理メソッドをインストールし、このエラーハンドラー内にエラー処理のコードを記述する必要があります:
$previousErrorHandler:=Method called on error
ON ERR CALL("errorOpenDoc")
var $fileHandle : 4D.FileHandle:=File($path).open()
If ($fileHandle#Null)
ON ERR CALL("errorReadDoc")
$text:=$fileHandle.readText()
If (Last errors#Null)
$text:="ファイル読み込みエラー"
End if
End if
ON ERR CALL($previousErrorHandler)
Try() キーワードを使用すると、同じセクションにすべてを記述することができ、ローカル変数にもアクセス可能で、コードを簡素化できます:
var $fileHandle : 4D.FileHandle:=Try(File($path).open())
If ($fileHandle#Null)
$text:=Try($fileHandle.readText()) || "ファイル読み込みエラー"
End if
ORDAのsave関数
もう 1 つの例として、予測可能なエラーと予測不可能なエラーを生成する可能性のある ORDA の save() 関数を考えてみましょう。
エンティティロックのような予測可能なエラーは、$result.success 属性を使って直接処理できます。しかし、主キーの重複のような予測不可能なエラーを処理するには、ON ERR CALLコマンドでエラーハンドラーをインストールする必要がありました (でないと、4D のエラーダイアログが表示されます):
var $customer : cs.CustomerEntity
// $customer エンティティでなにかしらの処理をおこないます
$previousErrorHandler:=Method called on error
ON ERR CALL("errorSaveManagement")
$result:=$customer.save()
ON ERR CALL($previousErrorHandler)
If (Not($result.success))
Case of
: ($result.status=3)
// ロックされたエンティティへの対応
: ($result.status=4)
// その他のエラーへの対応
End case
End if
しかし、Try() キーワードを使うことで、エラーダイアログを表示することなく、予測可能なエラーと予測不可能なエラーの両方を処理することが容易になります:
var $customer : cs.CustomerEntity
// $customer エンティティでなにかしらの処理をおこないます
$result:=Try($customer.save())
If (Not($result.success))
Case of
: ($result.status=3)
// ロックされたエンティティへの対応
: ($result.status=4)
// その他のエラーへの対応
End case
End if
式の実行
Try() キーワードは、変数・代入・メソッド・関数など、括弧で囲まれた任意の 4D 式を受け付けます。エラーが発生した場合、実行フローは停止し、直前の Try()キーワード (コールスタックで最初に見つかったもの) に戻ります。その結果、式の実行後にエラースタックをチェックするだけで、エラーが発生したかどうかを知ることができます。
なお、throw() コマンドを遅延モードで使用してエラーが生成された場合には、実行フローは現在のメソッド/関数の実行が終了するまで継続され、エラーの伝播を簡単に管理することができます。
戻り値
実行に成功すると、Try() は式の結果を返します。
エラーの場合、式の最新の結果が返され、結果が使用できない場合は ‘undefined’ が返されます。
式が戻り値を定義していない場合 (戻り値のないメソッドやコマンドなど)、try キーワードは ‘undefined’ を返します。
エラースタックについて
式が実行される前にエラースタックはクリアされ、Try() コンテキスト内でのエラー処理のために白紙に戻されます。式の実行中に発生したエラーはエラースタックに追加され、デバッグのための包括的な情報をデベロッパーに提供します。
エラー処理
ON ERR CALLコマンドによってカレントエラーハンドラーが定義されていない場合でも、Try() を使用すれば、エラーダイアログによってユーザー体験が妨げられることはありません。
Try() ブロックに入る前に設定されたグローバルおよびローカルなエラーハンドラーも呼び出されないため、Try のコンテキストとコードの他の部分がきれいに分離されます。
式内でカレントデータベースのグローバルまたはローカルなエラーハンドラーを定義している場合には、実行中にエラーが発生したときにそのエラーハンドラーが呼び出されます。
エラー処理の新たな可能性
この新機能は、エラー処理の可能性を広げるものです。しかしそれは、複数行の try-catch 文への道を開く、4D のエラー処理に向けた第2ステップに過ぎません!
フォーラムであなたの考えや経験をシェアし、この新機能についてどう思うか教えてください。
ハッピー・コーディング!