例外とは何か?
比較的新しいプログラム言語を使っている方々には、例外は使い慣れた定番の機能と言えるだろう。しかし、少し古いプログラム言語には、例外の機能がないことも多いので、今回はそのような言語に慣れたプログラマ向けに例外を解説してみたい。
例外は、処理の流れで特殊な状況が発生したときに起こる状況である。一般的には、各種エラー発生時などに例外は発生する。例外を発生することを「例外を投げる」ともいう。まずは、実際に例外が起きる事例を見ていただこう。以下は、存在しないファイルを開こうとするサンプル・プログラムだ。当然、正常に動作はしない。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace ConsoleApplication34
6: {
7: class Class1
8: {
9: static void Main(string[] args)
10: {
11: StreamReader reader = new StreamReader("存在しない.txt",Encoding.GetEncoding("Shift_JIS"));
12: Console.Write( reader.ReadToEnd() );
13: reader.Close();
14: }
15: }
16: } |
|
例外を発生させるサンプル・プログラム1 |
「存在しない.txt」というファイル名のファイルを開こうとしている。 |
これを実行すると以下のようになる。
|
サンプル・プログラム1の実行結果 |
プログラムを実行すると、例外が発生したことを知らせるメッセージが出力される。 |
|
|
例外の内容 |
|
|
スタック・トレース |
|
「未処理の例外」という文字列から、下から2行目の「line 11」までが例外によって出力されたメッセージである。これらのメッセージは例外が起きたことを報告するメッセージであって、例外そのものではない。「未処理の例外」から以下3行が例外の内容を伝えている。その下はスタック・トレースと呼ばれているもので、下から順番に読むものである。この例外が発生したのは、Mainメソッドから呼ばれたStreamReaderのコンストラクタ(「ctor」という表記になっている)から呼ばれた引数違いのコンストラクタの……、と繰り返し、最終的に“WinIOError”というクラスが発生源だと分かる。しかし、実際に自分で書いたのはMainメソッドだけなので、Mainメソッド内で何かトラブルが起きたと理解すればよいだろう。つまり、Mainメソッドから呼び出したStreamReaderクラスのコンストラクタで、何らかの問題が生じたと理解すればよい。具体的な問題の内容は最初の3行に示されているとおりで、ファイルが見つからなかったのである。
例外と他の方法を比較する
ファイルが見つからないときにどう対処するか、C言語とVisual Basci 6.0で記述したサンプル・ソースと比較してみよう。まずはC言語から。
1: #include <stdio.h>
2:
3: int main(int argc, char* argv[])
4: {
5: FILE * fp = fopen("存在しない.txt","r");
6: if( NULL == fp ) {
7: perror( "存在しない.txt" );
8: return 2;
9: }
10: while(1) {
11: int ch = fgetc(fp);
12: if( ch == -1 ) break;
13: putchar(ch);
14: }
15: fclose(fp);
16: return 0;
17: } |
|
C言語によるサンプル・プログラム2 |
fopen関数でファイルを開こうとしている。ファイルがオープンできたかどうかはfopen関数の戻り値でチェックする。 |
このサンプル・ソースのポイントは、6行目にある。5行目のfopen関数で開こうとしたファイルが存在しなかった場合、fopen関数はNULLを返すので、6行目のif文がエラーの発生を判断する。もし、if文を取り去ってしまうと、プログラムはエラー状態のまま実行を続け、正常な結果を出力しないまま終わるだろう。
次はVisual Basic 6.0の場合だ。
1: Private Sub Form_Load()
2: Dim s As String
3: On Error GoTo errorHandler
4: Open "存在しない.txt" For Input As #1
5: On Error GoTo 0
6: Do
7: If EOF(1) Then Exit Do
8: Line Input #1, s
9: Debug.Print s
10: Loop
11: Close 1
12: Exit Sub
13: errorHandler:
14: If Err = 53 Then
15: MsgBox "ファイルが見つかりません。"
16: Else
17: MsgBox "ファイルが開けません。"
18: End If
19: End Sub |
|
Visual Basic 6.0によるサンプル・プログラム3 |
ファイルがオープンできなかった場合には“On Error GoTo”文によりジャンプする。 |
ここでポイントになるのは、3行目の“On Error GoTo”文だ。これは、エラー発生時に実行すべきラベルを指定する機能である。もし、4行目のOpen文でファイルが見つからなかった場合、3行目の設定により、処理は13行目以降にジャンプする。このときもし、3行目がなければ、システムがエラー・メッセージを出力する。C言語のようにチェックするためのIf文は必要なく、チェックしなくてもエラー状態のまま実行が続くことはない。
では、これらとほぼ同等の機能をC#で記述するとどうなるだろうか。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace ConsoleApplication35
6: {
7: class Class1
8: {
9: static void Main(string[] args)
10: {
11: try
12: {
13: StreamReader reader = new StreamReader("存在しない.txt",Encoding.GetEncoding("Shift_JIS"));
14: Console.Write( reader.ReadToEnd() );
15: reader.Close();
16: }
17: catch( FileNotFoundException e )
18: {
19: Console.WriteLine("ファイル" + e.FileName + "が見つかりません。");
20: }
21: }
22: }
23: } |
|
C#言語によるサンプル・プログラム4 |
try文とcatch文のペアにより、ファイルがオープンできなかった場合の処理を記述する。 |
これを実行すると以下のようになる。
|
サンプル・プログラム4の実行結果 |
例外発生のメッセージは出力されず、ファイルのオープンができない場合のエラー・メッセージが正しく表示されている。
|
見てのとおり、ここにはif文はない。つまり、C言語のように、プログラマがエラーが発生したかどうかの判定をいちいち挿入する必要はない、ということだ。その点で、C言語よりもVisual Basicのスタイルに近い。ここでは、try文とcatch文のペアが、エラー発生時の動作を指定する手段を実現している。tryの後のブロック({ }の範囲内)には、エラーが起こるかもしれないコードを記述する。そして、catchの後のブロック中に、エラーが起こった場合の動作を記述する。13行目でファイルを開こうとしているが、ファイルが見つからない場合、即座に19行目に処理が移り、メッセージがコンソールに出力される。もし、try文とcatch文がなければ、最初のサンプルのように、システムが自動的にコンソールに情報を出力してプログラムを中断して終了する。
C#では、このようなエラーなどを例外と呼ぶ。try文は例外が発生するかもしれないコードを指定する役割を持ち、try文とペアで記述されるcatch文が、実際に発生した例外をつかまえて処理する役割を持つ。17行目のcatch文の括弧内に“FileNotFoundException e”と記述されているが、“FileNotFoundException”はクラス名で、“e”は引数の名前である。発生した例外的な状況には、それぞれ対応するクラスがあり、そのインスタンスが渡される。インスタンスには有益な情報が入っている。例えば、FileNotFoundExceptionはファイルが見つからないという状況を示すクラスで、見つからなかったファイルのファイル名を含むFileNameプロパティを持っている。これを使って適切なエラー・メッセージを組み立てることができる。
Insider.NET 記事ランキング
本日
月間