|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 09 例外処理
株式会社ピーデー
川俣 晶
2004/06/10 |
|
|
VB 6では、ErrorステートメントやErr.Raiseメソッドを用いてエラーを意図的に発生させることができることは、前節で述べた。このとき、あえてエラーが定義されていない番号を指定することもできる。これは自作プログラム内で発生する各種独自エラーを、VB 6のエラー処理メカニズムを利用して処理するために使用される。これをうまく使えば、プログラムをきれいにまとめられる可能性がある。リスト9-49に、独自エラーを、VB 6のエラー処理機能を用いて処理した例を示す。
1: Private Sub checkLicense()
2: If Dir("c:\user\license.txt") = "" Then
3: Err.Raise 1234
4: End If
5: End Sub
6:
7: Private Sub Form_Load()
8: On Error GoTo licenseErrorHandler
9: checkLicense
10: Exit Sub
11:
12: licenseErrorHandler:
13: If Err.Number <> 1234 Then
14: Err.Raise Err.Number
15: Else
16: MsgBox "ライセンスファイルがありません。使用するためにはライセンス認証が必要です。"
17: End
18: End If
19: End Sub
|
|
リスト9-49 VB 6のエラー処理機能を用いて独自のエラーを処理しているプログラム
|
これを実行すると以下のようになる。
|
●図9-50 リスト9-49の実行結果 |
このソース・コードは、3行目のErr.Raiseメソッドによって独自エラーを発生させている。このソースでは、適当に与えた1234という番号によって、独自エラーを識別するようにしている。このエラーは、13行目のIfステートメントによる判断され、1234番以外は通常のエラー処理に移行し、1234番ならメッセージを出力して終了する。
では、これと同等のソース・コードがVB.NETでも記述できるだろうか? 以下は記述してみた例である(リスト9-51)。
1: Private Sub checkLicense()
2: If Dir("c:\user\license.txt") = "" Then
3: Err.Raise(1234)
4: End If
5: End Sub
6:
7: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
8: On Error GoTo licenseErrorHandler
9: checkLicense()
10: Exit Sub
11:
12: licenseErrorHandler:
13: If Err.Number <> 1234 Then
14: On Error GoTo 0
15: Else
16: MsgBox("ライセンスファイルがありません。使用するためにはライセンス認証が必要です。")
17: End
18: End If
19: End Sub
|
|
リスト9-51 リスト9-49と同等な処理を記述したVB.NETのプログラム
|
これを実行すると以下のようになる。
|
●図9-52 リスト9-51の実行結果 |
見てのとおり、ほとんど変わりないものが記述できる。しかし、VB.NETの場合、構造化例外処理を用いて処理するという選択もある。もし、構造化例外処理を使うとすれば、どのように記述できるだろうか? 実際に記述してみたのがリスト9-53の例である。
1: Class LicenseErrorException
2: Inherits Exception
3: Public Sub New(ByVal msg As String)
4: MyBase.New(msg)
5: End Sub
6: End Class
7:
8: Private Sub checkLicense()
9: If Dir("c:\user\license.txt") = "" Then
10: Throw New LicenseErrorException("ライセンスファイルがありません。")
11: End If
12: End Sub
13:
14: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
15: Try
16: checkLicense()
17: Catch ex As LicenseErrorException
18: MsgBox(ex.Message & "使用するためにはライセンス認証が必要です。")
19: End
20: End Try
21: End Sub
|
|
リスト9-53 リスト9-51を、構造化例外処理を用いて記述したプログラム
|
これを実行すると以下のようになる。
|
●図9-54 リスト9-53の実行結果 |
このソースのポイントは、1〜6行目で定義されたクラスである。このクラスは、Exception(System.Exception)クラスを継承した例外クラスである(継承については、継承とポリモーフィズムを参照)。これらのクラスは、システムが提供するDivideByZeroExceptionなどと同じように例外機能で使用することができる。実際に、10行目のThrowステートメントで例外を発生させ、17行目のCatchブロックでそれをキャッチしている。この例外クラスでは、例外を投げる際に引数に理由を示す文字列を記述することができる。このサンプル・ソースには「ライセンスファイルがありません」という状況しか存在しないが、ライセンスファイルが正しくなかったり、期限が切れているような場合にも、それなりのメッセージを表示させたい場合は、このような引数が有効である。もちろん、それぞれの理由ごとに例外クラスを作成して区別するという方法もある。
さて、念のため、細かい部分を説明しておこう。2行目のInheritsステートメントは、Exceptionクラスを継承したクラスであることを示している。3〜5行目はコンストラクタである。4行目のMyBaseは、スーパークラスのコンストラクタを呼び出すために使用される。つまり、4行目では、引数を親クラスのコンストラクタに引き渡しているわけである。Exceptionクラスにはメッセージを受け取って保持する機能があるので、メッセージの保存はそれに任せている。
あるエラーが発生した場合、それを別のエラーとして扱いたい場合がある。例えば、ファイルが見つからないときに、それを「ファイルが見つからない」というエラーとして処理するのではなく、何らかのパラメータエラーとして扱いたい場合があるだろう。つまり、プロシージャに渡すパラメータの値の問題であると捉えたい、というわけだ。そういう場合、1回エラーをトラップしてから、もう一度エラーを発生させるという方法が可能である。実際にVB 6でそれを記述した例をリスト9-55に示す。
1: Private Function GetData(ByVal filename As String)
2: On Error GoTo errorHandlerInCalc
3: Open "c:\" & filename & ".txt" For Input As #1
4: Close 1
5: Exit Function
6:
7: errorHandlerInCalc:
8: If Err.Number <> 53 Then
9: Err.Raise Err.Number
10: Else
11: Err.Raise 1234
12: End If
13: End Function
14:
15: Private Sub Form_Load()
16: On Error GoTo errorHandlerInMain
17: Debug.Print GetData("app_define_data")
18: Exit Sub
19:
20: errorHandlerInMain:
21: If Err.Number <> 1234 Then
22: Err.Raise Err.Number
23: Else
24: MsgBox "内部パラメータエラーです。"
25: End If
26: End Sub
|
|
リスト9-55 エラー処理の中で、さらにエラーを発生させているプログラム
|
これを実行すると図9-56のようになる。
|
●図9-56 リスト9-55の実行結果 |
このソースは、ほとんどそのままVB.NETでも動作する(リスト9-57)。
1: Private Function GetData(ByVal filename As String)
2: On Error GoTo errorHandlerInCalc
3: FileOpen(1, "c:\" & filename & ".txt", OpenMode.Input)
4: FileClose(1)
5: Exit Function
6:
7: errorHandlerInCalc:
8: If Err.Number <> 53 Then
9: Err.Raise(Err.Number)
10: Else
11: Err.Raise(1234)
12: End If
13: End Function
14:
15: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
16: On Error GoTo errorHandlerInMain
17: Trace.WriteLine(GetData("app_define_data"))
18: Exit Sub
19:
20: errorHandlerInMain:
21: If Err.Number <> 1234 Then
22: Err.Raise(Err.Number)
23: Else
24: MsgBox("内部パラメータエラーです。")
25: End If
26: End Sub
|
|
リスト9-57 リスト9-55をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
●図9-58 リスト9-57の実行結果 |
これを、構造化例外処理を用いて書き直すと以下のようになる(リスト9-59)。
1: Class InternalParameterErrorException
2: Inherits Exception
3: Public Sub New(ByVal msg As String, ByVal innerException As Exception)
4: MyBase.New(msg, innerException)
5: End Sub
6: End Class
7:
8: Private Function GetData(ByVal filename As String)
9: Try
10: FileOpen(1, "c:\" & filename & ".txt", OpenMode.Input)
11: FileClose(1)
12: Return "ファイルから読み出したつもりのダミーデータ"
13: Catch ex As System.IO.FileNotFoundException
14: Throw New InternalParameterErrorException("内部パラメータエラーです。", ex)
15: End Try
16: End Function
17:
18: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
19: Try
20: Trace.WriteLine(GetData("app_define_data"))
21: Catch ex As InternalParameterErrorException
22: MsgBox(ex.Message & " (" & ex.InnerException.Message & ")")
23: End Try
24: End Sub
|
|
リスト9-59 リスト9-57を、構造化例外処理を用いて記述したプログラム
|
これを実行すると以下のようになる。
|
●図9-60 リスト9-59の実行結果 |
ここで結果を見比べて欲しい。構造化例外処理を使った例では、具体的に見つからなかったファイルのファイル名も表示されている。これは例外オブジェクトのInnerExceptionプロパティを利用することで実現されている。InnerExceptionプロパティは、ある例外を発生させる原因となった別の例外についての情報を保持することができる。つまり、1つの例外に対応する例外オブジェクトは1つに限られることはなく、その例外の原因となった例外に対応するオブジェクトも保持し続けることができる。もちろん、原因の例外オブジェクトにもInnerExceptionプロパティがあるので、保持できる例外の個数は2個に限定されない。
これに対応するために、3〜5行目のコストラクタは、innerExceptionを引数に持つ形で実装し、4行目でExceptionクラスのコンストラクタにもinnerExceptionの情報を引き渡している。そして、14行目で例外を再発生させる際、引数に原因となる例外オブジェクトを指定する。この情報は、21行目でキャッチされるときに引き継がれており、22行目のようにex.InnerExceptionとすることで参照できる。つまり、ex.Messageは例外を説明する文字列を持ち、ex.InnerException.Messageは原因となった例外を説明する文字列を持つ。
非構造化例外処理で使用されるErrオブジェクトは、システム内に1個しか存在しないため、新しいエラーが発生すると内容が上書きされてしまうのに対して、構造化例外処理の例外オブジェクトは複数のオブジェクトが存在できるので、このような使い方もできるのである。
業務アプリInsider 記事ランキング
本日
月間