フォーラム


ゲスト  

ようこそ ゲスト さん。このフォーラムに投稿するには 登録が必要です。

ページ: [1]
トピック: 例外に関するテクニック (DelWiki)
RAN
メンバー
投稿数: 34
例外に関するテクニック (DelWiki)
on: 2013/04/16 08:33 Tue

例外の種類を変更する
内部的なエラーによって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に投稿されていた物を復活した記事です。作者の方に感謝します。

ページ: [1]
WP Forum Server by ForumPress | LucidCrew
バージョン: 1.7.5 ; ページロード: 0.027 sec.