質問があります。この主観的なものにタグを付けるつもりです. 良いアイデアや示唆に富んだものを期待しています。長々とした質問で申し訳ありませんが、文脈を知る必要があります。
質問は基本的に次のとおりです。
- IoC コンテナーに関連して具象型をどのように処理しますか? 具体的には、それらを破棄する必要がある場合、誰がそれらを破棄する責任がありますか? また、その知識は呼び出し元のコードにどのように伝達されますか?
それらを IDisposable にする必要がありますか? そうでない場合、そのコードは将来性がありますか、それとも使い捨てオブジェクトを使用できないという規則ですか? インターフェイスと具象型に IDisposable-requirements を適用して将来的に保証する場合、コンストラクター呼び出しの一部として挿入されるオブジェクトは誰の責任ですか?
編集: @Chris Ballardの回答を受け入れました。これは、最終的なアプローチに最も近いものだからです。
基本的に、常に次のような型を返します。
public interface IService<T> : IDisposable
where T: class
{
T Instance { get; }
Boolean Success { get; }
String FailureMessage { get; } // in case Success=false
}
次に、このインターフェイスを実装するオブジェクトを .Resolve と .TryResolve の両方から返すので、呼び出しコードで取得するものは常に同じ型になります。
現在、このインターフェイスを実装するオブジェクトIService<T>
は IDisposable であり、常に破棄する必要があります。IService<T>
サービスを解決してオブジェクトを破棄するかどうかを決定するのは、プログラマの責任ではありません。
ただし、これは重要な部分であり、サービス インスタンスを破棄する必要があるかどうかに関係なく、その知識は を実装するオブジェクトに組み込まれているIService<T>
ため、ファクトリ スコープのサービスである場合 (つまり、Resolve への各呼び出しは新しいサービス インスタンスで終了します) )、IService<T>
オブジェクトが破棄されるときにサービス インスタンスが破棄されます。
これにより、プーリングなどの他の特別なスコープをサポートすることも可能になりました。これで、最小で 2 つのサービス インスタンス、最大で 15、通常は 5 が必要であると言えます。これは、.Resolve を呼び出すたびに、使用可能なオブジェクトのプールからサービス インスタンスを取得するか、新しいインスタンスを構築することを意味します。その後、IService<T>
プールされたサービスを保持するオブジェクトが破棄されると、サービス インスタンスはそのプールに解放されます。
確かに、これによりすべてのコードは次のようになります。
using (var service = ServiceContainer.Global.Resolve<ISomeService>())
{
service.Instance.DoSomething();
}
しかし、これはクリーンなアプローチであり、使用中のサービスや具体的なオブジェクトのタイプに関係なく同じ構文を使用するため、受け入れ可能なソリューションとしてこれを選択しました。
後世のために、元の質問が続きます
長い質問がここに来ます:
使用している IoC コンテナーがあり、最近、何が問題になるかを発見しました。
IoC 以外のコードで、たとえばファイルを使用したい場合、次のようなクラスを使用しました。
using (Stream stream = new FileStream(...))
{
...
}
ファイルを閉じる必要があることはわかっており、クラス自体が IDisposable を実装していたため、このクラスが限られたリソースを保持するものであったかどうかは疑問の余地がありませんでした。ルールは、IDisposable を実装する、オブジェクトを構築するすべてのクラスを破棄する必要があるという単純なものです。質問はありません。Dispose の呼び出しがオプションかどうかを決定するのは、このクラスのユーザー次第ではありません。
では、IoC コンテナーに向けた最初のステップに進みましょう。コードがファイルと直接やり取りするのではなく、1 つの間接的なレイヤーを通過することを望んでいないと仮定しましょう。この例では、このクラスを BinaryDataProvider と呼びましょう。内部的には、クラスはまだ使い捨てオブジェクトであるストリームを使用しているため、上記のコードは次のように変更されます。
using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
...
}
これはあまり変わりません。クラスが IDisposable を実装しているという知識はまだここにあります。質問はありません。Dispose を呼び出す必要があります。
しかし、現時点ではそのような限られたリソースを使用しないデータを提供するクラスがあると仮定しましょう。
上記のコードは、次のように記述できます。
BinaryDataProvider provider = new BinaryDataProvider();
...
OK、ここまでは順調ですが、ここからが本題です。特定の具象型に依存するのではなく、IoC コンテナーを使用してこのプロバイダーを注入するとします。
コードは次のようになります。
IBinaryDataProvider provider =
ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...
オブジェクトにアクセスできる独立したインターフェースが利用可能であると想定していることに注意してください。
上記の変更により、実際に破棄する必要があるオブジェクトを後で使用したい場合はどうなるでしょうか? そのインターフェイスを解決する既存のコードは、オブジェクトを破棄するように記述されていません。
私たちの見方では、解決策を 1 つ選択する必要があります。
- 登録されている具象型が IDisposable を実装しているかどうかを確認する実行時チェックを実装し、それを介して公開されるインターフェイスも IDisposable を実装する必要があります。これは良い解決策ではありません
- 使用されるインターフェイスに制約が課せられる前に、それらは常に IDisposable から継承する必要があります。
- 具体的な型を IDisposable にできないランタイムを強制します。これは、IoC コンテナーを使用するコードによって具体的に処理されないためです。
- オブジェクトがIDisposableを実装して「正しいことをする」かどうかをチェックするのはプログラマーに任せてください。
- 他にもありますか?
また、コンストラクターにオブジェクトを注入するのはどうですか? 私たちのコンテナ、および調査した他のコンテナのいくつかは、具象型のコンストラクターのパラメーターに新しいオブジェクトを注入することができます。たとえば、インターフェイスBinaryDataProvider
を実装するオブジェクトが必要な場合ILogging
、これらのオブジェクトに IDispose-"機能" を適用する場合、ロギング オブジェクトを破棄するのは誰の責任でしょうか?
どう思いますか?良くも悪くも意見が欲しいです。