ServiceContainer,IoC和一次性对象

ang*_*son 7 idisposable inversion-of-control disposable

我有一个问题,我将标记这个主观,因为这是我认为它演变成更多的讨论.我希望有一些好主意或一些思想家.我为这个冗长的问题道歉,但你需要了解背景.

问题基本上是:

  • 您如何处理与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
}
Run Code Online (Sandbox Code Playgroud)

然后我们从.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();
}
Run Code Online (Sandbox Code Playgroud)

但它是一种干净的方法,无论使用何种服务或具体对象,它都具有相同的语法,因此我们选择它作为可接受的解决方案.


对于子孙后代,原始问题如下


冗长的问题来到这里:

我们有一个IoC容器,最近我们发现了一个问题.

在非IoC代码中,当我们想要使用一个文件时,我们使用了这样的类:

using (Stream stream = new FileStream(...))
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

毫无疑问,这个类是否是一个资源有限的东西,因为我们知道必须关闭文件,并且类本身实现了IDisposable.规则很简单,我们构造实现IDisposable的对象的每个类都必须被处理掉.无话可问.这个类的用户不能决定调用Dispose是否是可选的.

好的,那么迈向IoC容器的第一步.让我们假设我们不希望代码直接与文件对话,而是通过一层间接.对于这个例子,我们将这个类称为BinaryDataProvider.在内部,类正在使用流,它仍然是一次性对象,因此上面的代码将更改为:

using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这没有太大变化.该类实现IDisposable的知识仍在这里,没有问题,我们需要调用Dispose.

但是,我们假设我们有提供数据的类,现在不使用任何这样有限的资源.

上面的代码可以写成:

BinaryDataProvider provider = new BinaryDataProvider();
...
Run Code Online (Sandbox Code Playgroud)

好的,到目前为止一切顺利,但问题的关键在于此.假设我们想要使用IoC容器来注入此提供程序,而不是依赖于特定的具体类型.

代码将是:

IBinaryDataProvider provider =
    ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...
Run Code Online (Sandbox Code Playgroud)

请注意,我假设有一个可用的独立接口,我们可以通过它访问该对象.

通过上述更改,如果我们以后想要使用真正应该处理的对象怎么办?没有解析该接口的现有代码被写入处理对象,那么现在呢?

我们看待它的方式,我们必须选择一个解决方案:

  • 实现运行时检查,检查正在注册的具体类型是否实现IDisposable,要求它通过它公开的接口也实现IDisposable.这不是一个好的解决方案
  • 在对所使用的接口进行约束之前,它们必须始终从IDisposable继承,以便面向未来
  • 强制运行时没有具体类型可以是IDisposable,因为使用IoC容器的代码没有特别处理
  • 只需将其留给程序员来检查对象是否实现了IDisposable并"做正确的事"?
  • 还有其他人吗?

那么,在构造函数中注入对象呢?我们的容器以及我们研究过的其他一些容器能够将新对象注入到具体类型的构造函数的参数中.例如,如果我们BinaryDataProvider需要一个实现ILogging接口的对象,如果我们对这些对象强制执行IDispose-"ability",那么它的职责是处理日志记录对象?

你怎么看?我想要意见,无论好坏.

kro*_*old 2

(免责声明:我是基于 java 的东西来回答这个问题的。虽然我编写 C# 程序,但我没有在 C# 中代理任何内容,但我知道这是可能的。对 java 术语感到抱歉)

您可以让 IoC 框架检查正在构造的对象以查看它是否支持 IDisposable。如果没有,您可以使用动态代理来包装 IoC 框架向客户端代码提供的实际对象。此动态代理可以实现 IDisposable,以便您始终向客户端传递 IDisposable。只要您使用的界面应该相当简单?

那么当对象是 IDisposable时,您就会遇到与开发人员沟通的问题。我不太确定如何以一种很好的方式完成这件事。