ただ単に例外をキャッチして処理を行うだけでは不十分な場合がある。例えば、1つの例外について、別々の階層のメソッド内の別々のcatch文でエラー処理を行う必要が生じる場合がある。このような場合、catchブロック内で、さらにもう一度同じ例外を発生させる必要が出てくる。このようなケースでは専用の構文が使用できる。List 18-12はそれを使用したサンプル・ソースである。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace Sample010
6: {
7: class DelayedFileReader
8: {
9: private string _fileName;
10: public DelayedFileReader( string fileName )
11: {
12: _fileName = fileName;
13: if( !File.Exists( fileName ) )
14: {
15: throw new FileNotFoundException( "ファイルが見つかりません。", fileName );
16: }
17: }
18: }
19: class Class1
20: {
21: private static void sample()
22: {
23: try
24: {
25: DelayedFileReader reader = new DelayedFileReader("存在しない.txt");
26: }
27: catch( FileNotFoundException e )
28: {
29: Console.WriteLine( "Class1.sampleでFileNotFoundException例外を確認しました。" );
30: throw;
31: }
32: }
33: [STAThread]
34: static void Main(string[] args)
35: {
36: try
37: {
38: sample();
39: }
40: catch( FileNotFoundException e )
41: {
42: Console.WriteLine( "Class1.MainでFileNotFoundException例外を確認しました。" );
43: }
44: }
45: }
46: }
これを実行するとFig.18-10のようになる。
List 18-12では、15行目で発生する例外を27行目からのcatchブロックでキャッチしているが、ここでキャッチしなければ40行目からのcatchブロックでもキャッチされるはずのものである。両方でエラー処理を行いたい場合は、27行目からのcatchブロック内でもう一度同じ例外を発生させ、40行目からのcatchブロックに実行するチャンスを与える必要がある。そのために、例外を再発生させているのが30行目のthrow文だが、見てのとおり、同じ例外を再発生させる場合は例外インスタンスを指定する必要はない。
例外で使用するクラスは、Exceptionクラスを継承していればよい。つまり、自分でExceptionクラスを継承したクラスを作れば、それも利用できる。実際に記述してみた例がList 18-13である。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace Sample011
6: {
7: class MyException : Exception
8: {
9: public MyException( string message ) : base( message )
10: {
11: }
12: }
13: class Class1
14: {
15: [STAThread]
16: static void Main(string[] args)
17: {
18: throw new MyException("自作例外");
19: }
20: }
21: }
これを実行するとFig.18-11のようになる。
Fig.18-11を見て分かるとおり、MyExceptionはシステムから例外の一種として認知されており、問題なくコンソールに情報が出力されている。Exceptionクラスには3つのコンストラクタがあるが、よく使われるのは文字列の引数が1個のものである。これは9行目のように、引数をちゃんと親クラスに渡してやらないとエラーになってしまうので、そのように記述する必要がある。それ以外は、プログラマーが必要だと思う機能をこのクラスに書き加えてよい。
このような自作例外クラスを用意すると自作プログラム内のエラー処理を円滑に行える場合もあるので、覚えておくとよいだろう。例え、自作例外クラス内に機能らしい機能が必要とされていないとしても、例外を区別できるというだけでメリットがある場合も多い。
いうまでもなく、例外クラスのインスタンスはインスタンスであるからメソッドやプロパティを持つ。Exceptionクラスには有益な情報を得られるメソッドやプロパティがあるので、それを使ってみよう。List 18-14を見てほしい。
1: using System;
2:
3: namespace Sample012
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: try
11: {
12: throw new Exception("Sample");
13: }
14: catch( Exception e )
15: {
16: Console.WriteLine(e.Message);
17: Console.WriteLine();
18: Console.WriteLine(e.Source);
19: Console.WriteLine();
20: Console.WriteLine(e.StackTrace);
21: Console.WriteLine();
22: Console.WriteLine(e.TargetSite);
23: Console.WriteLine();
24: Console.WriteLine(e.ToString());
25: }
26: }
27: }
28: }
これを実行するとFig.18-12のようになる。
List 18-14の16行目以降とFig.18-12を対比しながら見ていただきたい。まず、Messageプロパティは、例外発生時に指定されたメッセージを持つ。Sourceプロパティは例外の起きたプログラムの名前(正確にはアセンブリの名前)を持つ。場合によってはオブジェクトの名前になる場合もあるようだ。StackTraceはメソッドの呼び出された順番を示す文字列を持つ。例外をキャッチしないときに表示される、メソッドを羅列する文字列である。TargetSiteプロパティは、例外の発生したメソッドの情報を持つ。最後のToStringメソッドはシステムが例外の内容をコンソールに出力する場合と同じ書式の文字列を返す。もし、ログファイルなどに例外の情報を保存しておきたい場合は、このメソッドで情報を取得して、それをファイルに保存するようにするとよいだろう。
システムに含まれる主立った例外をTable 18-1に紹介する。
例外 | 意味 |
---|---|
System.OutOfMemoryException | メモリを使い切った |
System.StackOverflowException | スタックがあふれた |
System.NullReferenceException | nullへの参照にアクセスしようとした |
System.TypeInitializationException | staticなコンストラクタが例外を発生させたが、だれもキャッチしなかった |
System.InvalidCastException | キャストの間違い |
System.ArrayTypeMismatchException | 配列の型が合わない |
System.IndexOutOfRangeException | 添え字の範囲から外れている |
System.ArithmeticException | 数値演算エラー(ゼロ除算やオーバーフローなど) |
System.DivideByZeroException | ゼロ除算 |
System.OverflowException | オーバーフロー |
Table 18-1 |
念のために補足すると、ゼロ除算はArithmeticExceptionとDivideByZeroExceptionの両方でキャッチできるように読めるのは、そのとおりである。DivideByZeroExceptionはArithmeticExceptionを継承しているので、ArithmeticExceptionをキャッチしようとすれば、自動的にDivideByZeroExceptionもキャッチ可能になるのである。OverflowExceptionも事情は同じである。このように例外クラスを階層化してカテゴライズするのも、例外をエレガントに使う戦略の1つである。
『新プログラミング環境 C#がわかる+使える』
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て一部分を転載したものです。
【本連載と書籍の関係について 】
この書籍は、本フォーラムで連載した「C#入門」を大幅に加筆修正し、発行されたものです。連載時はベータ版のVS.NETをベースとしていましたが、書籍ではVS.NET製品版を使ってプログラムの検証などが実施されています。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。
→技術評論社の解説ページ
ご注文はこちらから
Copyright© Digital Advantage Corp. All Rights Reserved.