連載

改訂版
プロフェッショナルVB.NETプログラミング

Chapter 09 例外処理

株式会社ピーデー 川俣 晶
2004/06/10


 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。

構造化例外処理と非構造化例外処理の混用

 構造化例外処理と非構造化例外処理の機能を同時に使うことができるだろうか? 例えば、上記のサンプル・ソースでエラーの種類を判定するために、Errオブジェクトが使えるだろうか? リスト9-9はそれを確認するために記述したサンプル・ソースである。

 1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 2:   Dim i As Integer
 3:   For i = -2 To 2
 4:     Dim j As Integer
 5:     Try
 6:       j = 10 \ i
 7:     Catch ex As Exception
 8:       If Err.Number = 11 Then
 9:         Trace.WriteLine("∞")
10:       Else
11:         Throw
12:       End If
13:     End Try
14:     Trace.WriteLine(j)
15:   Next
16: End Sub
リスト9-9 構造化例外処理と非構造化例外処理の機能を同時に使用したプログラム

 これを実行すると以下のようになる。ただし、システムからのメッセージは除いてある。

1: -5
2: -10
3:
4: -10
5: 10
6: 5
リスト9-10 リスト9-9の実行結果

 構造化例外処理の中で、非構造化例外処理用のErrオブジェクトを使用してエラー番号を調べているが、確かに判定が実行されている。つまり、例外クラスの名前ではなく、エラー番号によるエラーの種類の判定が実現できている。

 しかし、このような方法はあまりお勧めではない。現在はたまたま動いているが、VB.NETの将来バージョンで動作するか分からないし、そもそも、将来的に非構造化例外処理がサポートされなくなる可能性も、決して小さくはない。できるだけ、構造化例外処理だけで記述するように心掛けるほうがよいだろう。

On Error Resume Next

 VB 6には、On Error Resume Nextステートメントがある。これは、エラーが発生してもそのまま次のステートメントに動作を進めるというラフな機能を指定する。当然、エラーが起こるとデタラメな動作が続くだけの無意味なプログラムになってしまう。では、On Error Resume Nextステートメントの存在意義は何かというと、エラーが発生する可能性のあるステートメントの実行直後に、Errオブジェクトを参照して、エラーの有無を確認するようなプログラミングスタイルを実現することである。実際にVB 6で、このようなスタイルで記述したソースをリスト9-11に示す。

 1: Private Sub Form_Load()
 2:   On Error Resume Next
 3:   Dim i As Integer
 4:   For i = -2 To 2
 5:     Dim j As Integer
 6:     j = 10 / i
 7:     If Err = 11 Then
 8:       Debug.Print "∞"
 9:       Err.Clear
10:     End If
11:     Debug.Print j
12:   Next
13: End Sub
リスト9-11 On Error Resume Nextステートメントを使用したプログラム

 これを実行すると以下のようになる。

1: -5
2: -10
3:
4: -10
5:  10
6:  5
リスト9-12 リスト9-11の実行結果

 こうすると、On Error Gotoを使用する場合より処理が直線的になって、流れを追いやすい。しかし、プログラマーがあらかじめ想定していないエラーが発生した場合には、そのまま処理が続く危険もある。

 さて、このソースをできるだけ忠実にVB.NET上に再現したものがリスト9-13である。

 1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 2:   On Error Resume Next
 3:   Dim i As Integer
 4:   For i = -2 To 2
 5:     Dim j As Integer
 6:     j = 10 \ i
 7:     If Err.Number = 11 Then
 8:       Trace.WriteLine("∞")
 9:       Err.Clear()
10:     End If
11:     Trace.WriteLine(j)
12:   Next
13: End Sub
リスト9-13 リスト9-11をVB.NETで再現したプログラム

 これを実行すると以下のようになる。ただし、システムからのメッセージは除いてある。

1: -5
2: -10
3:
4: -10
5: 10
6: 5
リスト9-14 リスト9-13の実行結果

 見てのとおり、ほとんど変わらないソース・コードが使用できることが分かるだろう。しかし、On Error Resume Nextステートメントは、非構造化例外処理の機能の1つである。では、構造化例外処理を使って、どのように記述すればそれに相当するソース・コードが作成できるだろうか。

 結論をいえば、On Error Resume Nextステートメントに相当する機能は、構造化例外処理には含まれていない。これは、すべてのエラー処理を暗黙のうちに決め打ちしてしまうことが危険だから、と解釈すべきだろう。起こり得るエラーに対する対応コードを、すべて明示的にTryステートメントとCatchブロックで表現することは、プログラムの品質を向上させるうえで意味があることである。

複数のエラーを扱う

 1つのTryステートメントに記述できるCatchブロックは、1つに限られない。つまり、1つのTryステートメントで、複数の例外クラスをキャッチすることができる。これを行う実例のサンプル・ソースを示そう。まず、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
リスト9-15 複数のエラーを同時に扱うプログラム

 これを実行すると以下のようになる。

1: ファイルが見つかりません: c:\存在しないファイル.txt
2: ディレクトリが見つかりません: c:\存在しないディレクトリ\test.txt
リスト9-16 リスト9-15の実行結果

 さて、非構造化例外処理を用いれば、VB.NETでこれとほぼ同等のソースを記述できることが予測できるだろう。では、構造化例外処理を使用して書き直した場合、どのような内容になるだろうか。実際に記述してみたのがリスト9-17である。

 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
リスト9-17 リスト9-15をTry〜Catchにより書き換えたVB.NETのプログラム

 これを実行すると以下のようになる。ただし、システムからのメッセージは除いてある。

1: ファイルが見つかりません: c:\存在しないファイル.txt
2: ディレクトリが見つかりません: c:\存在しないディレクトリ\test.txt
リスト9-18 リスト9-17の実行結果

 ここで注目すべきことは、1つのTryステートメントに対応するCatchブロックが2つ存在することである。このように、1つのTryステートメントに対応するCatchブロックはいくつでも記述することができる

 さて、主要なテーマではないが、6行目のex.FileNameについても少しだけ説明しよう。System.IO.FileNotFoundExceptionクラスは、見つからなかったファイルのファイル名を保持するFileNameプロパティを持っている。これを参照することで、例外処理の中で、エラー・メッセージを組み立てることが容易になる。このように、例外オブジェクトも種類によって、内容に相違がある場合がある。これは、1種類のErrオブジェクトですべてのエラーを扱う非構造化例外処理に対する長所といえる。


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 09 例外処理
    1.On Error Gotoを使用した非構造化例外処理/Try〜Catchを使用した構造化例外処理
  2.構造化例外処理と非構造化例外処理の混用/On Error Resume Next/複数のエラーを扱う
    3.例外情報の活用/ネストした例外処理
    4.例外の再発生/プログラムにより明示的に例外を発生させる
    5.例外の自作/InnerExceptionについて
    6.構造化例外処理の確実な終了処理/Finallyブロックの確実性/深い階層からの例外とFinallyブロック
 
「改訂版 プロフェッショナルVB.NETプログラミング 」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH