++C++; // 未確認飛行 C

Google
Web ufcpp.net

リソースの破棄

目次

キーワード

概要

ファイルや周辺機器などのリソース(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();
    }
  }
}

using 文

リソースの破棄の手順をまとめると以下のようになります。 (ただし、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');
      }
    }
  }
}
Transtation into English

[お問い合わせ](q)