連載

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

第14回 エラーと例外処理

(株)ピーデー
川俣 晶
2002/08/24

Page1 Page2 Page3

複数のエラーを扱う

 すでに説明したことではあるが、複数のエラーをそれぞれ異なる対応で同時に扱う方法について、実例のサンプル・ソースを示そう。まず、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

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

 1: 0 で除算しました。
サンプル・プログラム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

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

 1: 0 で除算しようとしました。
サンプル・プログラム11の実行結果

 このソースを見ると、FileOpen関数が2個で、FileClose関数が2個であることが分かると思う。つまり数が一致している。このソースで注目すべき点は、15行目のFinally文である。Finally文は、Try文の後に、Catch文に続けて記述することができる。そして、Finally文以降に記述されたステートメントは、Try文から処理が抜け出すときに必ず実行される。つまり、正常終了でも例外発生でも、条件に関係なく、必ず最後に実行されることが保証されている。この場合、11行目の計算が正常に終了しても、DivideByZeroExceptionクラスの例外を発生させても、あるいは、ほかのいかなる例外が発生しても、このTry文のブロックから抜け出すときには必ず実行されることが保証されている。

 これにより、開いたファイルを確実に閉じる、といった処理を容易に分かりやすく記述することができる。これは、非構造化例外処理と比較して、構造化例外処理の方が強力である理由の1つである。ファイルのクローズ忘れが確実に防止できるとしたら、それは非常にありがたいことではないだろうか?

次回予告

 次回も例外処理の解説を続ける予定である。End of Article


 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第14回 エラーと例外処理
    1.On Error Gotoを使う
    2.構造化例外処理と非構造化例外処理の混用
  3.複数のエラーを扱う
 
「プロフェッショナル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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間