以下は、disposeパターンにしたがって、IDisposableインターフェイスとファイナライザを実装するクラスResourceBase
と、それを継承したクラスExtendedResource
を作成する例です。
disposeパターンに従ったクラスの実装と継承の例
using System;
using System.IO;
using System.Runtime.InteropServices;
// 使用後に解放されるべきリソースを扱うクラス
class ResourceBase : IDisposable {
private bool disposed = false; // リソースが破棄(解放)されていることを表すフラグ
private IntPtr ptr = Marshal.AllocHGlobal(4); // コンストラクタ等で確保されるアンマネージリソースを想定したオブジェクト
// ファイナライザ
~ResourceBase()
{
// アンマネージリソースのみを破棄させる
Dispose(false);
}
// IDisposable.Disposeの実装
public void Dispose()
{
// アンマネージリソースと、マネージリソースの両方を破棄させる
Dispose(true);
// すべてのリソースが破棄されているため、以後ファイナライザの実行は不要であることをガベージコレクタに通知する
GC.SuppressFinalize(this);
}
// リソースの解放処理を行うためのメソッド
protected virtual void Dispose(bool disposing)
{
// 既にリソースが破棄されている場合は何もしない
if (disposed) return;
// アンマネージリソースと、マネージリソースの両方の破棄が要求された場合
if (disposing) {
// このクラスはマネージリソースを持たないので、ここですべきことは無い
}
// 破棄されていないアンマネージリソースの解放処理を行う
if (ptr != IntPtr.Zero) {
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
// リソースは破棄されている
disposed = true;
}
// 既にリソースが破棄されているかチェックして、ObjectDisposedExceptionをスローするためのメソッド
protected void ThrowIfDisposed()
{
if (disposed) throw new ObjectDisposedException(GetType().FullName);
}
// 保持しているリソースを使って何らかの操作を行うメソッドを想定
public virtual void UseResource()
{
// 既にリソースが破棄されているかチェックし、破棄されていればObjectDisposedExceptionをスローする
ThrowIfDisposed();
// リソースが利用可能な場合はそれを使った操作を行う
}
}
// IDisposableとファイナライザが実装された型を継承したクラス
class ExtendedResource : ResourceBase {
private IntPtr ptr = Marshal.AllocHGlobal(8); // コンストラクタ等で確保されるアンマネージリソースを想定したオブジェクト
private Stream stream = Stream.Null; // コンストラクタ等で確保されるマネージリソースを想定したオブジェクト
// 基底クラスのリソース解放処理をオーバーライド
// (ファイナライザ・IDisposable.Disposeメソッドは直接オーバーライドせず、このメソッドで実装する)
protected override void Dispose(bool disposing)
{
try {
// アンマネージリソースと、マネージリソースの両方の破棄が要求された場合
if (disposing) {
// 破棄されていないマネージリソースの解放処理を行う
if (stream != null) {
stream.Close(); // Disposeメソッド、あるいはそれを呼び出すメソッドを呼び出して解放する
stream = null;
}
}
// 破棄されていないアンマネージリソースの解放処理を行う
if (ptr != IntPtr.Zero) {
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
}
finally {
// 例外が発生しても基底クラスのDisposeメソッドを確実に呼び出すようにする
base.Dispose(disposing);
}
}
public override void UseResource()
{
// 既にリソースが破棄されているかチェックし、破棄されていればObjectDisposedExceptionをスローする
ThrowIfDisposed();
// 以降、リソースが利用可能な場合はそれを使った操作を行う
}
}
ここでは例示のためハンドル・ポインタをそのまま扱っていますが、可能ならSafeHandleでラップするようにします。 このほか、個々の事項については以下を参照してください。