制御の逆転と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を実装しなくてはならない、というのが面倒かもしれない。
なお、自動バインド機能(自動ワイアリング機能)があるコンテナを使用する場合、インタフェース・インジェクションに拘る必要は無く、プロパティインジェクションや、フィールドインジェクションを使っても良いと思う。