我有一个问题,我要标记这个问题主观因为这就是我认为它演变成的,更多的讨论。我希望能有一些好的想法或者一些发人深省的想法。我对这个冗长的问题表示歉意,但你需要了解上下文。
问题基本上是:
- 您如何处理与 IoC 容器相关的具体类型?具体来说,谁负责处置它们,如果它们需要处置,以及如何将这些知识传播到调用代码?
您是否要求它们是 IDisposable 的?如果不是,该代码是否可以适应未来,或者是不能使用一次性物品的规则?如果您在接口和具体类型上强制执行 IDisposable 要求以确保面向未来,那么谁的责任是作为构造函数调用的一部分注入对象?
Edit: 我接受了答案@克里斯·巴拉德 https://stackoverflow.com/users/18782/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,并且应该always被处置。解决服务是否由程序员来决定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 容器的第一步。假设我们不希望代码直接与文件对话,而是通过一层间接的方式。在本示例中,我们将此类称为 BinaryDataProvider。在内部,该类使用流,它仍然是一次性对象,因此上面的代码将更改为:
using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
...
}
这并没有太大改变。类实现IDisposable的知识还在这里,没有问题,我们需要调用Dispose。
但是,假设我们有提供数据的类,但目前不使用任何此类有限的资源。
上面的代码可以写成:
BinaryDataProvider provider = new BinaryDataProvider();
...
好的,到目前为止一切顺利,但问题的核心来了。假设我们想要使用 IoC 容器来注入此提供程序,而不是依赖于特定的具体类型。
代码将是:
IBinaryDataProvider provider =
ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...
请注意,我假设有一个独立的接口可用,我们可以通过它访问该对象。
通过上述更改,如果我们稍后想要使用真正应该处理的对象怎么办?解析该接口的现有代码都不是为了处理该对象而编写的,那么现在怎么办?
从我们的角度来看,我们必须选择一种解决方案:
- 实现运行时检查,检查正在注册的具体类型是否实现 IDisposable,并要求其公开的接口也实现 IDisposable。这不是一个好的解决方案
- 对所使用的接口进行约束,它们必须始终继承自 IDisposable,以便面向未来
- 强制运行时,任何具体类型都不能是 IDisposable,因为这不是由使用 IoC 容器的代码专门处理的
- 只需让程序员检查该对象是否实现 IDisposable 并“做正确的事情”?
- 还有其他人吗?
另外,在构造函数中注入对象怎么样?我们的容器以及我们研究过的其他一些容器能够将新对象注入到具体类型的构造函数的参数中。例如,如果我们的BinaryDataProvider
需要一个实现的对象ILogging
接口,如果我们对这些对象强制执行 IDispose-“能力”,谁的责任是处置日志记录对象?
你怎么认为?我想要意见,好的和坏的。