連載

プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――

第14回 エラーと例外処理

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

Page1 Page2 Page3

On Error Gotoを使う

 VB 6(Visual Basic 6.0)では、エラー発生時のプログラムの挙動を思いどおりにコントロールするために、「On Error Goto」という構文を使用する。これは筆者が使っていた昔懐かしい1979年発売のNEC PC-8001のN-BASICにも搭載されていたほど歴史の古い構文である。しかし、実際VB 6でプログラミングしていると、ほかの機能が進歩しているのに対して、エラー処理だけはOn Error Gotoという古い構造がそのまま使われている点が非常にアンバランスであった。例えば、On Error Gotoは行番号やラベルを必須とするため、構造化と相性が悪いし、エラーの種類を調べるために主に番号を用いるのもソースの分かりやすさを損なう。

 VB.NET(Visual Basic .NET)でも、On Error Gotoが使用できないわけではない。しかし、VB.NETには新しいエラー処理の機能である「構造化例外処理」と呼ばれるものが新たに加わった。これに対して、従来のOn Error Gotoは、「非構造化例外処理」と呼ばれるようになった。

 では、具体的にVB 6のエラー処理コードがどう変わるか、実例を見てみよう。まずVB 6で記述したサンプル・ソースから。

  1: Private Sub Form_Load()
  2:   On Error GoTo errorHandler
  3:   Dim i As Integer
  4:   For i = -2 To 2
  5:     Dim j As Integer
  6:     j = 10 \ i
  7:     Debug.Print j
  8:   Next
  9:   On Error GoTo 0
 10:   Exit Sub
 11:
 12: errorHandler:
 13:   If Err = 11 Then
 14:     Debug.Print "∞"
 15:     Resume Next
 16:   End If
 17:   Error Err
 18: End Sub
「On Error Goto」によりエラー処理をしたVB 6のサンプル・プログラム1

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

 1: -5
 2: -10
 3:
 4: -10
 5:  10
 6:  5
サンプル・プログラム1の実行結果

 このソースプログラムは6行目の割り算で0除算のエラーを発生する可能性がある。その場合、2行目で指定したOn Error Goto文によって12行目以降が実行される。そして、13行目のIf文で、発生したのが0除算かどうかを確認し、そうなら∞記号を表示して、エラーが発生した次のステートメントから実行を再開させている。再開は15行目のResume文で行っている。

 では、なるべくこれに近い形で、VB.NETで実行可能にするとどうなるだろうか。実際に記述してみたコードは以下のとおりである。

  1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  2:   On Error GoTo errorHandler
  3:   Dim i As Integer
  4:   For i = -2 To 2
  5:     Dim j As Integer
  6:     j = 10 \ i
  7:     Trace.WriteLine(j)
  8:   Next
  9:   On Error GoTo 0
 10:   Exit Sub
 11:
 12: errorHandler:
 13:   If Err.Number = 11 Then
 14:     Trace.WriteLine("∞")
 15:     Resume Next
 16:   End If
 17:   Error Err.Number
 18: End Sub
サンプル・プログラム1に近い形で記述したVB.NETのサンプル・プログラム2

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

 1: -5
 2: -10
 3:
 4: -10
 5: 10
 6: 5
サンプル・プログラム2の実行結果

 見てのとおり、ほとんど同じとなっている。特に目立つのは13行目で、デフォルト・プロパティがないため、ErrオブジェクトのNumberプロパティを明示的に記述する必要が生じている。遠い昔のBASIC言語の知識でErrは数値を持つシステム変数だと思っていた方は、知識をアップデートする必要があるだろう。

 ただし、1つだけ注意すべきことがある。実は、このコードは、6行目の「\」を「/」に変更すると、VB 6とVB.NETで挙動が変わってしまう。VB 6は「\」を「/」に変更しても発生するエラーは11番(0除算)だが、VB.NETでは「\」を「/」に変更すると6番(オーバーフロー)が発生する。表面的な構文は同じだが、必ず同じ番号のエラーをつかまえられるわけではないことに注意が必要だろう。

Try〜Catchを使う

 すでに述べたとおり、VB.NETには構造化例外処理というものがある。VB 6でOn Error Gotoを用いて作成されたソース・コードは、VB.NETではOn Error Goto(非構造化例外処理)ではなく、構造化例外処理を使用するように書き直すことができる。以下は実際に構造化例外処理を使用して書き直した例である。

  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 DivideByZeroException
  8:       Trace.WriteLine("∞")
  9:     End Try
 10:     Trace.WriteLine(j)
 11:   Next
 12: End Sub
サンプル・プログラム2を構造化例外処理を使用して記述したVB.NETのサンプル・プログラム3

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

 1: -5
 2: -10
 3:
 4: -10
 5: 10
 6: 5
サンプル・プログラム3の実行結果

 何もかも変わってしまったように見えるが、機能的にはまったく等価である。

 On Error Gotoは、有効なラベルを指定したOn Error Gotoから、On Error Goto 0までの範囲でエラーの発生を検出するが、構造化例外処理では、Tryステートメントから、Catchステートメントの範囲でエラーの発生を検出する。エラーが発生すると、On Error Gotoの場合は指定ラベルに処理がジャンプするが、構造化例外処理では、CatchステートメントからEnd Tryステートメントまでの範囲が実行される。こう書くと、あまり変わらないように思うかもしれないが、ソース中でOn Error Goto〜On Error Goto 0で挟んだ範囲と、Try〜Catchで挟んだ範囲がまるで違っていることが分かるだろう。前者はかなり広い範囲を挟んでいるが、後者は1行しか挟んでいない。その相違の理由は何だろうか?

 その理由は、例外処理終了時に処理を元の流れに戻すことができないからだ。On Error Gotoを使用しているときは、Resume文で簡単に元の処理の流れに戻ることができる。しかし、構造化例外処理にはそのような機能が存在しない。元の処理の流れに戻るためには、End Tryの次に、戻るべき処理の流れが存在していなければならない。そこで、必然的にエラーを検出する範囲が変わってしまうのである。

 次に注目すべき点は、発生したエラーの種類を判定するIf文がどこにもないことである。エラーの種類は、Catch文の後に書かれた引数によって指定されている。ここでは、DivideByZeroExceptionという名前がエラーの種類を示している。これは、0除算を意味する例外クラスの名前だが、Catch文に記述されることにより、0除算だけを受け止めるという効能を発揮する。つまり、

Catch 〜 As DivideByZeroException

と記述していれば、常に0除算の場合のみCatch文以降を実行し、それ以外のエラーは何も指定されなかったのと同じ結果になるのである。そのため、構造化例外処理を使用する場合には、エラーの種類を判定するIf文は不要となり、また、エラーの種類は番号ではなくクラス名で判別される。

 最後に7行目の引数として指定されたexという名前はどんな意味があるのだろうか。これはVB 6のErrオブジェクトに似た役割を持ったオブジェクトが格納される引数である。このソースでは参照していないが、少し込み入った処理では参照が必要になる。

 余談だが、あえてVB 6の場合のように、Catch文の中でエラーの種類を判定するように記述するとすれば、どうなるだろうか? 以下はそれを記述してみた例である。

  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 TypeOf ex Is DivideByZeroException Then
  9:         Trace.WriteLine("∞")
 10:       Else
 11:         Throw
 12:       End If
 13:     End Try
 14:     Trace.WriteLine(j)
 15:   Next
 16: End Sub
Catch文の中でエラーの種類を判定するようにしたVB.NETのサンプル・プログラム4

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

 1: -5
 2: -10
 3:
 4: -10
 5: 10
 6: 5
サンプル・プログラム4の実行結果

 このソース・コードで注目すべき点は、7行目のCatch文で指定されたデータ型が、DivideByZeroExceptionではなく、Exceptionになっている点である。エラーを表す例外クラスにはそれぞれ継承関係があり、スーパー・クラスの名前を指定しておけば、サブ・クラスの例外もキャッチすることができる。すべての例外クラスは、System.Exceptionクラスを継承して作られているので、このクラスを指定しておけば、すべての例外をキャッチすることができる。なお、ここでは“System.”を省略して“Exception”とだけ書いている。

 次に注目してほしいのは、引数exが活用されている点である。8行目のIf文で、TypeOfキーワードを用いて、引数exの方が、DivideByZeroExceptionかどうかを調べている。Catch文の引数がException型だからといって、exに含まれている内容が本当にException型とは限らない。もしかしたら、Exceptionクラスを継承した別のクラスのインスタンスかもしれないのである。

 もう1点見ておきたいのが、11行目のThrow文である。Throw文は例外を明示的に発生させる機能を持ったもので、非構造化例外処理のError文あるいはErrオブジェクトのRaiseメソッドに相当する。これについてはあらためて取り上げるが、Catch文内で引数なしで使用すると、Catchされた例外を再発生する効能がある。

 なお、このようなすべての例外をまとめてキャッチする方法は、実際にはあまり使われない。なぜなら、1つのTry文に対応するCatch文はいくつでも書けるので、If文で種類を判定しなくても、Catch文を増やすだけで済むからである。そのようなサンプル・ソースは後で示す。


 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 記事ランキング

本日 月間