IDisposableインターフェイスは、それを実装する型が適切に破棄・解放されるべきリソースを保持していることを表すと同時に、それを明示的に破棄させる手段を提供するためのインターフェイスです。 IDisposableインターフェイスには一つのメソッドDisposeがあり、このメソッドを呼び出すことでリソースを破棄・解放できるようにします。
ファイナライザによるリソースの破棄では、ファイナライザが実行されるタイミングを制御できない、常に実行されるとは限らないという問題があります。 アンマネージリソースを扱うクラスなど、リソースを任意のタイミングで速やかに破棄・解放できるようにしたい場合には、IDisposableインターフェイスを実装します。
以下は、IDisposableインターフェイスを実装する例です。 この例では、適切に解放すべきリソースの一例としてMarshal.AllocHGlobalでメモリ領域を確保し、IDisposableインターフェイスによって解放できるようにしています。
あくまでIDisposableインターフェイスのみを主眼においた実装となっているため、例外時等の考慮は十分ではない点に注意してください。 より適切な実装については§.ファイナライザとIDisposableのデザインパターン (disposeパターン)を参照してください。
using System;
using System.Runtime.InteropServices;
class UnmanagedMemory : IDisposable {
public IntPtr Ptr; // アンマネージメモリ領域を参照するポインタ
public UnmanagedMemory(int size)
{
// メモリ領域を確保する
Ptr = Marshal.AllocHGlobal(size);
}
// IDisposable.Disposeの実装
public void Dispose()
{
if (Ptr == IntPtr.Zero)
return; // すでに解放されている場合は何もしない(二重解放の抑止)
// 確保されているメモリ領域を解放する
Marshal.FreeHGlobal(Ptr);
// 解放済みであることを示すためにIntPtr.Zeroを設定する
Ptr = IntPtr.Zero;
}
}
class Sample {
static void Main()
{
// IDisposableインターフェイスを実装するオブジェクトを作成する
// (本来ならusingステートメントを使ったほうがよいが、例示のため省略)
var mem = new UnmanagedMemory(4);
Marshal.WriteInt32(mem.Ptr, 16);
// オブジェクトが必要なくなった時点でDisposeメソッドを呼び出し、アンマネージリソースを解放する
mem.Dispose();
}
}
IDisposableインターフェイスでは、Disposeメソッドが複数回呼び出された場合でも正しく動作するように実装する必要があります。 具体的には、リソースの二重解放が行われないようにするなどの考慮が必要です。 また、Disposeメソッドからは後述の例外ObjectDisposedExceptionをスローしないようにします。
IDisposableインターフェイスには、すでに破棄されているかどうかを知るためのIsDisposed
のようなプロパティはありません。 このことからも、Disposeメソッドが複数回呼び出されても正しく動作するようにする必要があります。
Disposeメソッドを呼び出す時点でそのオブジェクト自体必要なくなっているケースがほとんどですが、IsDisposed
のようなプロパティが必要ならば、Disposeメソッドが呼び出された時点で内部的にフラグを立て、それを返すプロパティを用意するなどします。