連載
プロフェッショナルVB.NETプログラミング
第14回 エラーと例外処理
(株)ピーデー
川俣 晶
2002/08/24
|
|
複数のエラーを扱う
すでに説明したことではあるが、複数のエラーをそれぞれ異なる対応で同時に扱う方法について、実例のサンプル・ソースを示そう。まず、VB 6でファイルを開く際、ファイルが見つからない場合とディレクトリが見つからない場合の処理を記述した例を見てみよう。
1: Private Sub test(ByVal s As String)
2: On Error GoTo errorHandler
3: Open s For Input As #1
4: Close #1
5: Exit Sub
6:
7: errorHandler:
8: If Err = 53 Then
9: Debug.Print "ファイルが見つかりません: " & s
10: ElseIf Err = 76 Then
11: Debug.Print "ディレクトリが見つかりません: " & s
12: Else
13: Err.Raise (Err)
14: End If
15: Exit Sub
16: End Sub
17:
18: Private Sub Form_Load()
19: test "c:\存在しないファイル.txt"
20: test "c:\存在しないディレクトリ\test.txt"
21: End Sub
|
|
複数のエラーを同時に扱うVB 6のサンプル・プログラム8 |
これを実行すると以下のようになる。
1: ファイルが見つかりません: c:\存在しないファイル.txt
2: ディレクトリが見つかりません: c:\存在しないディレクトリ\test.txt
|
|
サンプル・プログラム8の実行結果 |
さて、非構造化例外処理を用いれば、これとほぼ同等のソースがVB.NETで記述できることは当然予測できるだろう。では、構造化例外処理を使用して書き直した場合、どのような内容になるだろうか。実際に記述してみたのが以下のソース・コードである。
1: Private Sub test(ByVal s As String)
2: Try
3: FileOpen(1, s, OpenMode.Input)
4: FileClose(1)
5: Catch ex As System.IO.FileNotFoundException
6: Trace.WriteLine("ファイルが見つかりません: " & ex.FileName)
7: Catch ex As System.IO.DirectoryNotFoundException
8: Trace.WriteLine("ディレクトリが見つかりません: " & s)
9: End Try
10: End Sub
11:
12: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
13: test("c:\存在しないファイル.txt")
14: test("c:\存在しないディレクトリ\test.txt")
15: End Sub
|
|
サンプル・プログラム8をTry〜Catchにより書き換えたVB.NETのサンプル・プログラム9 |
これを実行すると以下のようになる。ただしシステムからのメッセージは除いてある。
1: ファイルが見つかりません: c:\存在しないファイル.txt
2: ディレクトリが見つかりません: c:\存在しないディレクトリ\test.txt
|
|
サンプル・プログラム9の実行結果 |
ここで注目すべきことは、1つのTry文に対応するCatch文が2つ存在することである。このように、1つのTry文に対応するCatch文はいくつでも記述することができる。
さて、主要なテーマではないが、6行目のex.FileNameについても少しだけ説明しよう。System.IO.FileNotFoundExceptionクラスは、見つからなかったファイルのファイル名を保持するFileNameプロパティを持っている。これを参照することで、例外処理の中で、エラーメッセージを組み立てることが容易になる。このように、例外オブジェクトも種類によって、内容に相違がある場合がある。これは、1種類のErrオブジェクトですべてのエラーを扱う非構造化例外処理に対する長所といえる。
構造化例外処理ならではの確実な終了処理
非構造化例外処理にあって構造化例外処理にはない機能があることは紹介したが、逆の事例もある。つまり、構造化例外処理にあって非構造化例外処理にはない機能も存在する。
まず、以下のVB 6のソースを見ていただきたい。
1: Private Sub Form_Load()
2: Open "c:\testtxt" For Output As #1
3: Write #1, 0
4: Close #1
5:
6: Open "c:\testtxt" For Input As #1
7: Dim i As Integer
8: Input #1, i
9:
10: On Error GoTo errorHandler
11: Debug.Print 1 \ i
12: On Error GoTo 0
13:
14: Close #1
15: Debug.Print "正常終了"
16: Exit Sub
17:
18: errorHandler:
19: Close #1
20: Debug.Print Err.Description
21: End Sub
|
|
複数の終了処理が必要となるVB 6のサンプル・プログラム10 |
これを実行すると以下のようになる。
このソースには、2個のOpen文と、3個のClose文がある。数が一致しないのは、6行目のOpen文に対応するClose文が2個あるためである。On Error Goto文でエラー処理を行った場合と正常終了を行った場合について、それぞれClose文が必要とされている。これはResume文を使って処理を戻すことで1つのClose文では済ませられない事例である。このような状況になると、うっかりCloseを忘れる場合があるので危険である。特に処理の流れが複数に分かれたとき、どの流れでも確実にCloseするように注意を払うのは大変であり、しばしば見落としが発生する。
このような問題をスマートに解決する方法が、VB.NETの構造化例外処理には用意されている。それを使った例を以下に示す。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: FileOpen(1, "c:\testtxt", OpenMode.Output)
3: Write(1, 0)
4: FileClose(1)
5:
6: FileOpen(1, "c:\testtxt", OpenMode.Input)
7: Dim i As Integer
8: Input(1, i)
9:
10: Try
11: Trace.WriteLine(1 \ i)
12: Trace.WriteLine("正常終了")
13: Catch ex As DivideByZeroException
14: Trace.WriteLine(ex.Message)
15: Finally
16: FileClose(1)
17: End Try
18: End Sub
|
|
Finally文を使用して終了処理を行うVB.NETのサンプル・プログラム11 |
これを実行すると以下のようになる。
このソースを見ると、FileOpen関数が2個で、FileClose関数が2個であることが分かると思う。つまり数が一致している。このソースで注目すべき点は、15行目のFinally文である。Finally文は、Try文の後に、Catch文に続けて記述することができる。そして、Finally文以降に記述されたステートメントは、Try文から処理が抜け出すときに必ず実行される。つまり、正常終了でも例外発生でも、条件に関係なく、必ず最後に実行されることが保証されている。この場合、11行目の計算が正常に終了しても、DivideByZeroExceptionクラスの例外を発生させても、あるいは、ほかのいかなる例外が発生しても、このTry文のブロックから抜け出すときには必ず実行されることが保証されている。
これにより、開いたファイルを確実に閉じる、といった処理を容易に分かりやすく記述することができる。これは、非構造化例外処理と比較して、構造化例外処理の方が強力である理由の1つである。ファイルのクローズ忘れが確実に防止できるとしたら、それは非常にありがたいことではないだろうか?
次回予告
次回も例外処理の解説を続ける予定である。
Insider.NET 記事ランキング
本日
月間