制御の逆転とIDisposable(後始末編)
制御の逆転とIDisposable (DIxAOPによる解決)の続き。
追記 : このままではちょっとしたまずいことが発生する。それに関してはまた別なエントリで。
含みを持たせた終わり方だったのだが、ここで書いたちょっとしたまずいこととは、IDisposableなオブジェクトのDisposeが実行されないことだ。
はあ? そもそもIDisposable且つSingletonなオブジェクトを、DIコンテナから提供する場合に、不意にDisposeを呼ばれることをnoopやthrowで無効にしたかったのは、ぬしではなかったか? ああその通り。
最終的にアプリケーションは終了し、DIコンテナも破棄すべき時になったら、今度は散々無視してきたDisposeを実行してやらないとまずい。アンマネジリソースを含んだオブジェクトが対象だった時は相当まずいことになる。
早速対策を、といってもさほど難しくは無い。インターセプタ実装クラスのInvokeメソッドで使用するパラメタ型IMethodInvocationインタフェースはプロキシでは無い、元のオブジェクトの参照を保持しているからだ。
またもやSeasar2風のコードで恐縮だが、前回書いたNoopInterceptorを修正する例を。
public class NoopInterceptor : AbstractInterceptor
{
private Stack disposableStack = new Stack();
public NoopInterceptor()
:base()
{
System.Windows.Forms.Application.ApplicationExit +=
new EventHandler(
delegate(object sender, EventArgs e)
{
foreach (IDisposable disposable in this.disposableStack)
{
disposable.Dispose();
}
});
}
public override object Invoke(IMethodInvocation invocation)
{
IDisposable disposable = invocation.Target as IDisposable;
if (disposable != null)
{
this.disposableStack.Push(disposable);
}
return null;
}
}
最終的にIDisposable.Dispose()を実行するタイミングはいろいろ考えられるが、元々はWindowsFormsが対象なので、アプリケーション終了時でよいだろう。ということでApplicationExitイベントで、過去に呼ばれたであろう、Dispose()を実行することにした。
noopされた(Dispose)メソッドの対象がネストすることを考えて、格納先をスタックにしてみたが、今回のケースでは特定の型にアスペクトを適用するのが解っているので、あまり意味が無いのかもしれない。
まだ他に問題や改良点があるのかもしれないが、今はこれしか思いつかなかった。期待に沿わなかったとしたら申し訳ない。