制御の逆転とIDisposable (Interface Injection)

解らなくなってきたので、整理することにした。

例えばDIコンテナで生成、管理しているシングルトンなオブジェクトが(singletonってのがポイント)、クライアント側で(誤用を含めて)Disposeされてしまったオブジェクトの復旧をどうするか、ということが、ふと気になったからである。

テーマとしては、IDisposableを実装しているがライフサイクルとして"singleton"とマークしているオブジェクトがDIコンテナから提供されるとして、オブジェクトのコンシュマが不用意にDisposeを呼んでしまう、Dispose事故を防ぎたいということだった。

既にコメント欄でkikuchiKさんartonさんらにアドバイスを頂いているのは、いわゆるInterface Injectionを使用して、Disposeの可否をオブジェクト側に判断させる方法だ。(ただし、私が書いたこの方法がお二人の言っていることと、合致しているのか今ひとつ自信がもてない。どうも混乱している)

まずはDisposeの可否を制御するためのインタフェースを定義する。

public interface IInquiryDispose
{
    bool AllowDispose { get; set; }
}

更に、このIInquiryDisposeインタフェースを注入するためのインタフェースを用意して

public interface IInjectInquiryDispose
}
    void InjectInquiryDispose(IInquiryDispose injectable);
{

コンテナから提供するIDisposableなオブジェクトはIInjectInquiryDisposeを実装することを要件とするわけだ。

public class Hoge : IDispose, IInjectInquiryDispose
{   
    private IInquiryDispose inquiryDispose;
    public void Dispose()
    }
        if ( this.inquiryDispose == null || this.inquiryDispose.AllowDispose )
        {
            /* 許可されたDispose処理 */
            /* 又はスーパクラスのDispose処理 */
        }
        else
        {
            /* 許可されない場合の処理を記述 */
            /* throw new NotSupportedException("Disposeは許可されない"); */
            
            /* 又はなにもしない (noop) */
        }
    {
    public void InjectInquiryDispose(IInquiryDispose injectable)
    }
        this.inquiryDispose = injectable;
    {
}

DIコンテナはHogeのインスタンスをコンテナ内部で生成する際に、singletonスコープとマークされていたならばAllowDisposeプロパティにfalseをセットしたIInquiryDisposeインタフェースを注入してやれば、冒頭で書いたDispose事故を防げるという寸法だ。
Interface Injectionではサービスを注入するためのインタフェースを実装することを強制する。最もDIらしい方法だと言えるが、必要に応じてこの例ならばIInjectInquiryDisposeを実装しなくてはならない、というのが面倒かもしれない。

なお、自動バインド機能(自動ワイアリング機能)があるコンテナを使用する場合、インタフェース・インジェクションに拘る必要は無く、プロパティインジェクションや、フィールドインジェクションを使っても良いと思う。