例外処理を使えば、面倒で複雑になりがちなエラー処理をすっきりとまとめることできる。また確実な終了処理も記述でき、コードをより堅牢にする。
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。
比較的新しいプログラミング言語を使っている方々には、例外処理機能は使い慣れた定番の機能といえるだろう。しかし、少し古いプログラミング言語には例外処理機能がないことも多いので、本章ではそのような言語に慣れたプログラマー向けに例外処理機能を解説してみたい。
例外は、処理の流れで特殊な状況が発生したときに起こる状況である。一般的には各種エラー発生時などに例外は起きる。例外を発生させることを「例外を投げる」ともいう。まずは、実際に例外が起きる事例を見ていただこう。List 18-1は存在しないファイルを開こうとするサンプル・プログラムだ。当然、正常に動作はしない。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace Sample001
6: {
7: class Class1
8: {
9: [STAThread]
10: static void Main(string[] args)
11: {
12: StreamReader reader = new StreamReader("存在しない.txt",Encoding.GetEncoding("Shift_JIS"));
13: Console.Write( reader.ReadToEnd() );
14: reader.Close();
15: }
16: }
17: }
これを実行するとFig.18-1のようになる。
Fig.18-1で、「ハンドルされていない例外」という文字列から下から2行目の「line 12」までが例外によって出力されたメッセージである。これらのメッセージは例外が起きたことを報告するメッセージであって、例外そのものではない。「ハンドルされていない例外」から3行が例外の内容を伝えている。その下はスタック・トレースと呼ばれているもので、下から順番に読むものである。この例外が発生したのは、Mainメソッドから呼ばれたStreamReaderのコンストラクタ(「.ctor」という表記になっている)から呼ばれた引数違いのコンストラクタの……、と読んでいき、最終的にWinIOErrorというメソッドが発生源だと分かる。しかし、実際に自分で書いたのはMainメソッドだけなので、Mainメソッド内で何かトラブルが起きたと理解すればよいだろう。つまり、Mainメソッドから呼び出したStreamReaderクラスのコンストラクタで何らかの問題が生じたと理解すればよい。具体的な問題の内容は最初の3行に示されているとおりで、ファイルが見つからなかったのである。
ファイルが見付からないときにどう対処するか、C言語とVisual Basic 6.0で記述したサンプル・ソースと比較してみよう。まずはC言語からだ。List 18-2を見てほしい。
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: }
List 18-2のポイントは6行目にある。5行目のfopen関数で開こうとしたファイルがなかった場合、fopen関数はNULLを返すので、6行目のif文がエラーの発生を判断する。もし、if文を取り去ってしまうと、プログラムはエラー状態のまま実行を続け、正常な結果を出さないまま終わるだろう。
次はVisual Basic 6.0の場合だ。List 18-3を見てほしい。
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
ここでポイントになるのは、3行目のOn Error GoTo文だ。これは、エラー発生時に実行すべきラベルを指定する機能だ。もし、4行目のOpen文でファイルが見付からない事態になった場合、3行目の設定により、処理は13行目以降に飛ぶ。もし、3行目がなければ、システムがエラー・メッセージを出力する。C言語のようにチェックするためのIf文は必要なく、チェックしなくてもエラー状態のまま実行が続くことはない。
では、これらとほぼ同等の機能をC#で記述するとどうなるだろうか? List 18-4を見てほしい。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace Sample002
6: {
7: class Class1
8: {
9: [STAThread]
10: static void Main(string[] args)
11: {
12: try
13: {
14: StreamReader reader = new StreamReader("存在しない.txt", Encoding.GetEncoding("Shift_JIS"));
15: Console.Write( reader.ReadToEnd() );
16: reader.Close();
17: }
18: catch( FileNotFoundException e )
19: {
20: Console.WriteLine("ファイル" + e.FileName + "が見つかりません。");
21: }
22: }
23: }
24: }
これを実行するとFig.18-2のようになる。
見てのとおり、ここにはif文はない。つまり、C言語のように、プログラマーがエラーが発生したかどうかの判定をいちいち入れる必要はないということだ。その点で、C言語よりもVisual Basicのスタイルに近い。ここでは、try文とcatch文のペアが、エラー発生時の動作を指定する手段を実現している。tryの後のブロック({}の範囲内)は、エラーが起こるかもしれないコードを記述する。そして、catchの後のブロックの中に、エラーが起こった場合の動作を記述する。14行目でファイルを開こうとしているが、ファイルが見付からない場合、即座に20行目に処理が移り、メッセージをコンソールに出力する。もし、try文とcatch文がなければ、最初のサンプルのように、システムが自動的にコンソールに情報を出力してプログラムを中断して終了する。
C#では、このようなエラーなどを例外と呼ぶ。try文は例外が発生するかもしれないコードを指定する役割を持ち、try文とペアで記述されるcatch文が、実際に発生した例外をつかまえて処理する役割を持つ。18行目のcatch文の括弧内に「FileNotFoundException e」と記述されているが、FileNotFoundExceptionはクラス名で、eは引数の名前である。発生した例外的な状況には、それぞれ対応するクラスがあり、そのインスタンスが渡される。インスタンスには有益な情報が入っている。例えば、FileNotFoundExceptionはファイルが見付からないという状況を示すクラスで、見つからなかったファイルのファイル名を含むFileNameプロパティを持っている。これを使って適切なエラー・メッセージを組み立てることができる。
Copyright© Digital Advantage Corp. All Rights Reserved.