IDisposable


from http://garicchi.hatenablog.jp/entry/2014/09/14/200000


IDisposable

IDisposableは簡単に言うと、実装すると、Disposeメソッドを実行することによってリソースを破棄してくれることを保証するインターフェースです。
ようするにクラスがもっているリソースをDisposeメソッドによって破棄する、C++のdeleteと似たところがあります。
しかし、C#にdeleteはなく、ガベージコレクションによってメモリが足りなくなると自然にリソースは破棄されます。
じゃあIDisposableインターフェース実装する必要ないやん!ってなりますがC#には.Netのガベージコレクションが解放してくれないリソースがあるのです。

マネージリソースとアンマネージリソース

.Netでは、C#で作ったクラスなどはマネージリソースとよび、.Netのガベージコレクションによってメモリがたりなくなりかつオブジェクトがどこからも参照されなくなると破棄されます。
いっぽう、ファイルの入出力なのでつかうファイルはアンマネージリソースとよび、.Netのガベージコレクションによって破棄されません。
IDisposableインターフェースは、このようなアンマネージリソースを破棄するための機能を実装するインターフェースです。
たとえば、StreamReaderなどのクラスを使うとき、このような形になります。 Closeメソッドが呼ばれた時、ファイルのアンマネージリソースは解放されます。
static void Main(string[] args)
{
try{
    StreamReader reader = new StreamReader("hoge.txt");
    string text=reader.ReadToEnd();
    reader.Close();
}catch(Exception){
}
    
}

ではreader.Closeを呼ぶ前に例外が発生したらどうなるでしょうか。
static void Main(string[] args)
{
try{
    StreamReader reader = new StreamReader("hoge.txt");
    string text=reader.ReadToEnd();
//例外発生
    reader.Close();
}catch(Exception){
}
    
}

Closeメソッドがよばれませんね。すると永久にhoge.txtのアンマネージリソースは解放されません。 これではダメなのでfinallyを使ってCloseを強制的に呼び出します。
static void Main(string[] args)
{
try{
    StreamReader reader = new StreamReader("hoge.txt");
    string text=reader.ReadToEnd();
//例外発生
}catch(Exception){
}finally{
    reader.Close();
}
    
}
さらにこれでもreaderがnullだった場合、nullpointerExceptionが出てしまいます。
このように非常にめんどくさいことになるのでC#ではIDisposableを実装することによってusing文をつかった統一的なアンマネージリソース処理方法があります。

using

C#では先程の処理をusingを使ってこんな感じにかくことができます。
using (StreamReader reader = new StreamReader("hoge.txt"))
{
    string text = reader.ReadToEnd();
}
まったく後処理がされていませんが、StreamReaderはIDisposableインターフェースを実装しているのでコンパイル時にこんな感じに最適化されます。
StreamReader reader=null;
try
{
    reader=  new StreamReader("hoge.txt");
    string text = reader.ReadToEnd();
}
catch (Exception e)
{
}
finally
{
    if (reader != null)
        reader.Dispose();
}
これでどこで例外が起きようが確実にDisposeメソッドを実行してアンマネージリソースを解放することができます。
なのでIDisposableインターフェースを実装しているクラスを使うときは、なるべくusing文法を使うことが推奨されています。

IDisposableを実装してみる

ではIDisposableを実装したDisposableStreamReaderというクラスを作ってみます。
public class DisposableStreamReader:IDisposable
{
    StreamReader reader = null;
    public DisposableStreamReader()
    {
        reader = new StreamReader("hoge.txt");
    }

    public string ReadAll()
    {
        return reader.ReadToEnd();
    }
    public void Dispose()
    {
        reader.Dispose();
    }
}
IDisposableインターフェースが実装を保証するのはDisposeメソッドです。ここでアンマネージリソースを解放します。
これを使うときはこんなかんじでusing文を使うことが可能になります。
static void Main(string[] args)
{
    using(DisposableStreamReader reader=new DisposableStreamReader())
    {
        string str= reader.ReadAll();
    }
}

まとめ

  • .Netにはマネージリソースとアンマネージリソースがある
  • IDisposableはDisposeメソッドでアンマネージリソースを解放することを保証する
  • IDisposableインターフェースを実装することによってusing文をつかうことができる


留言

熱門文章