確実な終了処理を行うfinally
上記のサンプル・ソースには大きな弱点がある。もし、StreamReaderクラスのコンストラクタで例外が起きず、ReadToEndメソッド内で例外が発生した場合、Closeメソッドが実行されずにファイルが開いたままになることだ。このサンプル・ソースはすぐにプログラム自身が終了してしまうので大した実害はないが、これが大きなプログラムの一部だとすると、使っていないファイルが開いたままになり、何か悪影響を及ぼす可能性がある。実際、ReadToEndメソッドは“IOException”を発生させる可能性がある。フロッピー・ディスクからの読み出し中にメディアが抜かれたような状況では、これが起こる可能性がある。
このような状況に対処するには、catch文の中でファイルを閉じるようにするのも1つの手段だが、もっとエレガントな方法が用意されている。それは、try文とペアで使うfinally文である。以下はそれを活用して、いかなる場合にも確実にファイルを閉じるように改良したものである。
1: using System;
2: using System.IO;
3: using System.Text;
4:
5: namespace ConsoleApplication39
6: {
7: class Class1
8: {
9: static void Main(string[] args)
10: {
11: StreamReader reader = null;
12: try
13: {
14: reader = new StreamReader("存在しない.txt",Encoding.GetEncoding("Shift_JIS"));
15: Console.Write( reader.ReadToEnd() );
16: }
17: catch( Exception e )
18: {
19: Console.WriteLine(e.GetType().FullName + "の例外が発生しました。");
20: }
21: finally
22: {
23: Console.WriteLine("終了処理を実行します。");
24: if( reader != null )
25: {
26: reader.Close();
27: }
28: }
29: }
30: }
31: } |
|
確実に終了処理を行うサンプル・プログラム8 |
try文とcatch文のペアに加えて、finally文を使用している。finally文では終了処理を記述する。 |
これを実行すると以下のようになる。
|
サンプル・プログラム8の実行結果 |
例外の発生いかんにかかわらず、finally文は必ず実行される。
|
ここで注目すべきは、try文にcatch文が続くだけでなく、さらにその後にfinally文が続いていることである。try-catch-finallyと続く流れは1つの定番である。finallyに続くブロックに記述されたコードは、tryブロック内の処理が正常に終わっても、何かの例外が発生しても、それらの条件に関係なく必ず実行される。つまり、ファイルを閉じるためのCloseメソッドをここに記述すれば(26行目)、例外が発生しようとしまいと、必ずファイルを閉じることができるのである。ただ、StreamReaderクラスのコンストラクタで例外が起きた場合は、ファイルが開かれる前の状態のままなので、その場合はCloseメソッドを呼ぶ意味がない。そのため、24行目のif文があり、ファイルを開いたあとか、開く前かを判断している。
finally文はbreak文やreturn文でも有効
finally文のブロックは、例外が起きても必ず実行されるだけではない。break文やreturn文で、tryブロックから直接抜け出すようなコードを書いても確実に実行される。以下はそれを示すサンプル・ソースである。
1: using System;
2:
3: namespace ConsoleApplication40
4: {
5: class Class1
6: {
7: static void Main(string[] args)
8: {
9: int [] array = { 1,2,3,4,5,6 };
10: foreach( int n in array )
11: {
12: try
13: {
14: if( n == 3 ) break;
15: Console.WriteLine(n);
16: }
17: finally
18: {
19: Console.WriteLine( n + "の終了処理が呼び出されました。(その1)" );
20: }
21: }
22: foreach( int n in array )
23: {
24: try
25: {
26: if( n == 3 ) return;
27: Console.WriteLine(n);
28: }
29: finally
30: {
31: Console.WriteLine( n + "の終了処理が呼び出されました。(その2)" );
32: }
33: }
34: }
35: }
36: } |
|
finally文の動作を示すサンプル・プログラム9 |
break文やreturn文によりループを途中で抜け出すような場合にもfinallyブロックは呼び出されるのか? |
これを実行すると以下のようになる。
|
サンプル・プログラム9の実行結果 |
“3の終了処理が呼び出されました…”と表示されていることから、ループを抜ける前にfinallyブロックが実行されているのが分かる。
|
14行目のif文により、nが3のとき、12行目から16行目までのtryブロックから直接break文で飛び出している。しかし、実行結果画面を見て分かるとおり、nが3のときのfinallyブロックはちゃんと実行されている。このfinallyブロックの実行後にforeach文を抜け出しているのである。同様に26行目のreturn文でも、メソッドからリターンする前に、29行目以降のfinallyブロックが実行されていることが分かるだろう。
このように、finally文は、例外とは無関係にも利用でき、確保した資源を確実に解放したい、という場合にも利用できる優れものである。その際、try文とfinally文だけ記述すればよく、catch文抜きでもかまわない。
Insider.NET 記事ランキング
本日
月間