例外の種類を変更する
内部的なエラーによってraiseされるさまざまな種類の例外(EConvertError, EInOutError, EWin32Error等々)をカスタム例外に変換してraiseする場合には以下のようなコードを書きます。
type EMyLibraryError = class(Exception); procedure ExecuteMyLibrary; begin try Win32Check(...); StrToInt(...); sl.LoadFromFile(...); ... except on E:Exception do raise EMyLibraryError.Create('MyLibraryは処理に失敗しました。'); end; end;
なぜ、どのような場合に例外の種類を変更するのか
Delphiにはあるメソッドからraiseされる例外を全て列挙することをサポートする文法・ツールはありません((Javaにはthrows構文が、Eclipseにはthrowされる例外をすべて列挙して例外ハンドラを自動生成する機能があります。))。その為一つのメソッドからあまりたくさんの種類の例外を生成されると非常にコーディングが煩雑になり、ドキュメントのメンテナンスが困難になります。そしてそのことで全ての例外を安易にon E:Exception doでハンドリングしてしまっては本末転倒です。ですから例外の種類は最小限に抑える必要があります((もちろん無条件に減らすことが正しいわけではありません。後述のクラスライブラリやフレームワークなどで「層」が変わる時に検討してください。))。
また、汎用的なクラスライブラリを作成する場合、そのライブラリ内の実装に依存した例外をアプリケーションがそのまま受け取っても適切に処理することが出来ません。必要なのはそのライブラリがデータのロードに失敗した、実行に失敗した、事前に渡されたパラメータが不正である等、ライブラリの仕様に即した例外です。
そこでライブラリが例外を受け取り、適切なカスタム例外に変換してアプリケーションに通知します。
例えばIndyでは以下のような独自の例外ツリーを定義しています。
Exception +EIdException +EIdAlreadyConnected +EIdSilentException +EIdInvalidServiceName +EIdProtocolReplyError +EIdDnsResolverError +EIdInvalidSocket +EIdSocketError ...
例外オブジェクトの解放について
例外ハンドラ内で別の例外をraiseした場合、以下の順で例外オブジェクトが解放されます。解放は全て自動的に行われるので明示的にFreeする必要はありません。
try try raise Exception1.Create('Exception1'); except on E:Exception do raise Exception2.Create('Exception2'); ①・・・E=Exception1のインスタンスが解放される end; except on F:Exception do ShowMessage(F.Message); ②・・・F=Exception2のインスタンスが解放される end;
内部例外を保存する
ある例外を発生させる原因となったもとの例外の事を内部例外(InnerException) と呼びます。
C#では内部例外を簡単に保存できます。GCがあると便利ですね。
public void ThrowInner () { throw new MyAppException("ExceptExample inner exception"); } public void CatchInner() { try { this.ThrowInner(); } catch (Exception e) { throw new MyAppException("Error caused by trying ThrowInner.",e); } } }
それに対してDelphiは・・・
try try raise EInternalError.Create('内部エラー'); except on E:Exceptiondo // HelpContextプロパティに内部例外を渡してみる raise EMyLlibraryError.CreateHelp('ライブラリエラー', Integer(E)); end; except on E:Exception do ShowMessage('エラー発生:' + E.Message + ' 内部例外:' + Exception(E.HelpContext).Message); // 既に内部例外は解放されてしまっているので正しく動作しない end;
内部例外の情報を伝えるには
Delphiで内部例外を通知するには情報を全てMessageなどのプロパティに変換して保存しなければなりません。あまり洗練されたやり方とはいえませんが、開発者に必要な情報を通知するには十分と言えるでしょう。
procedure ExecuteMyLibrary; begin try Win32Check(...); StrToInt(...); sl.LoadFromFile(...); ... except on E:Exception do raise EMyLibraryError.CreateFmt( 'MyLibraryは処理に失敗しました(内部例外:%s:%s)', [E.ClassName, E.Message]); end; end;
謝辞
2004年頃にDelWikiに投稿されていた物を復活した記事です。作者の方に感謝します。
|