Поскольку вы хотите использовать эту конструкцию в многопоточном приложении и хотите повторно использовать один и тот же экземпляр в потоках (как вы подразумеваете в своем комментарии), вы не сможете решить эту проблему, настроив свой контейнер 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;
}
}
Обратите внимание, что эта реализация все еще имеет недостатки из-за следующих причин:
- Вызов
Dispose
несколько раз прерывает работу декоратора. - Когда потребители вызывают
SomeOperation
несколько раз (или IService
имеет несколько методов), реализация прерывается.
Довольно сложно создать декоратор, который функционирует так, как ожидалось.Один из простых способов сделать это - сериализовать доступ к объекту, но когда вы делаете это, вы, вероятно, захотите использовать один экземпляр для каждого потока.Это было бы намного проще.
Надеюсь, это поможет.