ファイルや周辺機器などのリソース(OSが管理している資源)を使用する場合、 まずリソースを使用する権利を取得し、 リソースに対する操作(ファイルの読み書きなど)を行った後、 リソース使用権を破棄する必要があります。
メモリは .NET Framework のガベッジコレクション機能が自動的に管理していて、 プログラマが明示的に破棄してやる必要はないのですが、 ファイルなどはガベッジコレクションの管理対象外で、 明示的な破棄が必要です。
リソースの破棄を怠ると操作が正しく完了しなかったり、 他のプログラムからそのリソースを使用できなくなったりします。 (例えば、ファイルにロックが掛かったままになって、ファイルの読み書きがしばらくできなくなったり。) そのため、リソースの破棄は確実に行う必要があるのですが、 これは意外に面倒な作業だったりします。
例えば、ファイルの読み書きを行う場合、 まずファイルを開いて、読み書きを行った後、ファイルを閉じる必要があります。 以下に簡単な例を示します。
using System; using System.IO; class DisposeTest { static void Main(string[] args) { FileStream reader = new FileStream(args[0], FileMode.Open); // 先頭のNバイトを読み出して画面に表示 const int N = 32; byte[] buf = new byte[N]; reader.Read(buf, 0, N); for(int i=0; i<N; ++i) { Console.Write("{0,4}", (int)buf[i]); if(i%8 == 7) Console.Write('\n'); } reader.Close(); // ファイルを閉じる(リソースの破棄) } }
この例のようなリソース破棄の仕方には実は問題があります。
この例のコードでは例外が発生したときに Close メソッドが呼ばれないため、
リソースの開放が出来なくなります。
例外が発生した場合にも Close メソッドが呼ばれるようにするためには、
以下のように try-catch-finally 文を用います。
using System; using System.IO; class DisposeTest { static void Main(string[] args) { FileStream reader = null; try { reader = new FileStream(args[0], FileMode.Open); // 先頭のNバイトを読み出して画面に表示 const int N = 32; byte[] buf = new byte[N]; reader.Read(buf, 0, N); for(int i=0; i<N; ++i) { Console.Write("{0,4}", (int)buf[i]); if(i%8 == 7) Console.Write('\n'); } } catch(Exception) { // 例外処理を行う } finally { // 例外が発生しようがしまいが finally ブロックは必ず実行される。 // リソースの破棄は finally ブロックで行う。 if(reader != null) reader.Close(); } } }
リソースの破棄の手順をまとめると以下のようになります。
(ただし、Resource はリソース管理用クラスで、
Dispose メソッドによりリソースの破棄を行うものとする。)
Resource r = null; try { r = new Resource(); リソースに対する操作 } finally { if(r != null) r.Dispose(); }
リソースの破棄は必ずこの手順で行います。 しかし、毎回同じ手順を繰り返すのは面倒です。 そこで、C#ではこの手順を自動的に行ってくれる構文が用意されています。 この構文は using 文と呼ばれ、以下のようにして用います。
using(Resource r = new Resource()) { リソースに対する操作 }
using 文を用いると、
コンパイラが自動的に上述のリソース破棄用のコードに展開してくれます。
ただし、using 文で使うリソース管理用クラスは
System.IDisposable インターフェース
を実装している必要があります。
(FileStream などのクラスライブラリ中のクラスは System.IDisposable インターフェースを実装しています。)
using 文を用いて上述の例を書き直したものを以下に示します。
using System; using System.IO; class DisposeTest { static void Main(string[] args) { using(FileStream reader = new FileStream(args[0], FileMode.Open)) { // 先頭のNバイトを読み出して画面に表示 const int N = 32; byte[] buf = new byte[N]; reader.Read(buf, 0, N); for(int i=0; i<N; ++i) { Console.Write("{0,4}", (int)buf[i]); if(i%8 == 7) Console.Write('\n'); } } } }