質問
" static readonly T Instance = new T();"を使用するシングルトンがありますパターン。しかし、Tが使い捨てであり、実際に単体テストのために廃棄する必要がある場合に遭遇しました。このパターンを変更して使い捨てシングルトンをサポートするにはどうすればよいですか?
希望するインターフェースは次のようなものです:
var x = Foo.Instance;
var y = Foo.Instance; // x == y
...
x.Release(); // this causes the next Foo.Instance to return a fresh object
// also, it assumes no further operations on x/y will be performed.
注-もちろん、パターンはスレッドセーフでなければなりません。
編集-量産コードの目的上、これは真のシングルトンです。問題は、いくつかのファイルをロックすることです。そのため、単体テストでのクリーンアップのために、それを破棄する必要があります。
また、可能であれば、再利用できるパターンも好みます。
解決
Release
を internal
としてマークし、 InternalsVisibleTo
属性を使用して、単体テストアセンブリにのみ公開します。それを行うこともできますし、自分のアセンブリ内の誰かがそれを呼び出すことに注意している場合は、 private
としてマークし、リフレクションを使用してアクセスできます。
シングルトンインスタンスで Dispose
メソッドを呼び出すファイナライザをシングルトンで使用します。
製品コードでは、 AppDomain
をアンロードするだけでシングルトンが破棄されます。テストコードでは、自分で Release
の呼び出しを開始できます。
他のヒント
その時点では、正直に言うと、もうシングルトンだとは本当に思わないと思います。
特に、クライアントがシングルトンを使用している場合、それを破棄する必要があることを本当に期待することはなく、他の誰かが使用した場合は驚かれることでしょう。
本番コードはどうしますか?
編集:単体テストに本当に必要な場合、および単体テストにのみが必要な場合(これは、デザインの点で疑わしいように、率直に言って)、常にリフレクションを使用してフィールドをいじることができます。それが本当にシングルトンであるか、それとも本当に使い捨てであるかを判断する方が良いでしょう-2つはめったに一緒に行きません。
シングルトンは使い捨てであってはなりません。期間。誰かが早期にDisposeを呼び出すと、アプリケーションは再起動するまでねじ込まれます。
public class Foo : IDisposable
{ [ThreadStatic] static Foo _instance = null;
private Foo() {IsReleased = false;}
public static Foo Instance
{ get
{ if (_instance == null) _instance = new Foo();
return _instance;
}
}
public void Release()
{ IsReleased = true;
Foo._instance = null;
}
void IDisposable.Dispose() { Release(); }
public bool IsReleased { get; private set;}
}
クラスがIDisposableを実装している場合(暗黙のとおり)、x.Dispose()を呼び出すだけです
ネストされた遅延シングルトンを使用できます(こちらを参照)簡単な変更:
public sealed class Singleton : IDisposable
{
Singleton()
{
}
public static Singleton Instance
{
get
{
if (!Nested.released)
return Nested.instance;
else
throw new ObjectDisposedException();
}
}
public void Dispose()
{
disposed = true;
// Do release stuff here
}
private bool disposed = false;
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
オブジェクトが破棄されている場合、オブジェクトのすべてのパブリックメソッド/プロパティでObjectDisposedExceptionをスローすることを忘れないでください。
また、Disposeが呼び出されない場合に備えて、オブジェクトのファイナライザーメソッドを提供する必要があります。 IDisposable こちらを正しく実装する方法をご覧ください。
単体テストでは、「マニュアル」を使用できます。インスタンス(ただし、オブジェクトをインスタンス化する方法が必要です)。
あなたの場合、おそらくシングルトンと組み合わせて、ファクトリパターン(abstract / method-あなたのケースに最適な方)を使用することをお勧めします。
シングルトンが使用済みオブジェクトを適切に破棄したかどうかを単体テストでテストする場合は、Factoryメソッドを使用し、そうでない場合はシングルトンパターンを使用します。
ところで、シングルトンのソースコードにアクセスできない場合、または変更が許可されていない場合は、別のシングルトンにラップして、新しいシングルトンからのすべてのロジックを提供することをお勧めします(プロキシ)。やり過ぎのように聞こえますが、実行可能な解決策になる可能性があります。
また、アクセスを制御できるようにするために、ファクトリを提供し、オブジェクトが破棄されていない場合にのみクライアントが新しいオブジェクトを取得できるようにします。
使い捨てシングルトンを作成する別のオプションは、クラスにSandCastleの [Singleton] 属性を使用することです。その後、Castleフレームワークがすべての使い捨てシングルトンオブジェクトを破棄します