在 Ninject 中,在单例范围内声明绑定意味着每次都会返回相同的对象。永远只能有一个对象。
我想要的是一次返回一个对象。换句话说:
- 第一次调用 Get() 实例化一个新对象并返回它。
- 对 Get() 的后续调用返回相同的实例。
- 该对象已被处置。
- 对象被释放后对 Get() 的第一次调用会实例化一个新的/第二个对象并返回该对象。
- 对 Get() 的后续调用将返回在步骤 4 中创建的对象。
EDIT:这个问题实际上很容易解决,使用提供者 https://github.com/ninject/ninject/wiki/Providers,-Factory-Methods-and-the-Activation-Context并让相关对象在处置时引发事件。我很好奇是否有办法使用 Ninject 中的范围来做到这一点,并将这个问题留在这里,因为 Steven 的回答非常好。
由于您想在多线程应用程序中使用此构造,并希望跨线程重用同一实例(正如您在评论中暗示的那样),因此您将无法通过配置 DI 容器来解决此问题。
由于竞争条件,您根本无法将对象配置为在处置后更新。想象一下以下场景:
- 线程 1 向容器请求一个实例。
- 这是第一个请求,容器将创建一个新实例。
- 线程2向容器请求一个实例
- 容器返回在步骤 2 中创建的实例。
- 线程 1 完成实例并调用
Dispose
.
- 线程 2 开始使用该实例,但该实例已被释放,并引发异常。
问题是应用程序将获得对可以释放的实例的引用。
如果可以的话,尝试通过重新设计您的应用程序来防止这种情况发生。公开实现的服务类型是一种不好的做法IDisposable
, 因为IDisposable
是一个有漏洞的抽象。我个人的偏好甚至是阻止这些服务的任何实现IDisposable
。在大多数情况下,重新设计可以让您不必这样做。
如果您需要使用IDisposable
对象,通常的方法是创建并注入创建这些对象的工厂IDisposable
对象。这样,消费者就可以安全地处置此类物品,没有任何问题。
这里的普遍问题是很难创建实现IDisposable
,这实际上是线程安全的。
如果你真的想要这个,你可以尝试创建一个执行引用计数的装饰器。例如,看看下面的装饰器。它包裹着一个IService
并实施IService
. IService
实施IDisposable
。装饰器需要一个Func<IService
> 允许创建实例的委托。对象的创建和处置受到保护lock
语句和装饰器计算调用者对它的引用。在最后一个消费者处置装饰器之后,它将处置该对象并创建一个新对象。
public class ScopedServiceDecorator : IService
{
private readonly object locker = new object();
private Func<IService> factory;
private IService currentInstance;
private int referenceCount;
public ScopedServiceDecorator(Func<IService> factory)
{
this.factory = factory;
}
public void SomeOperation()
{
IService instance;
lock (this.locker)
{
instance = this.GetInstance();
this.referenceCount++;
}
instance.SomeOperation();
}
public void Dispose()
{
IService instance = null;
lock (this.locker)
{
this.referenceCount--;
if (this.referenceCount == 0)
{
instance = this.wrappedService;
this.wrappedService = null;
}
}
// Dispose the object outside the lock for performance.
if (instance != null)
{
instance.Dispose();
}
}
private IService GetInstance()
{
if (this.wrappedService == null)
{
this.wrappedService = this.factory();
}
return this.wrappedService;
}
}
请注意,由于以下原因,此实现仍然存在缺陷:
- Calling
Dispose
多次破坏装饰器。
- 当消费者致电
SomeOperation
多次(或IService
有多种方法)执行将会中断。
创建一个按预期工作的装饰器是相当困难的。执行此操作的一种简单方法是序列化对对象的访问,但是当您执行此操作时,您可能希望每个线程使用单个实例。那会容易得多。
我希望这有帮助。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)