Since you want to use this construct in a multi-threaded application and want to reuse the same instance in threads (as you mean in your comment), you cannot solve this problem by setting up a DI container.
You simply cannot configure the object that will be updated after deletion, due to race conditions. Imagine the following scenario:
- In stream 1, an instance from the container is requested.
- This is the first request, and the container will create a new instance.
- Thread 2 requests an instance from the container
- The container returns the instance created in step 2.
- Topic 1 runs on the instance and calls
Dispose . - Topic 2 is started using the instance, but the instance is deleted and throws an exception.
The problem is that the application will receive a link to an instance that can be deleted.
Try to prevent this by changing your application if you can. Bad practice is to expose the types of services that implement IDisposable , because IDisposable is a fuzzy abstraction. My personal preference is to even prevent the implementation of these services to implement IDisposable . In most scenarios, a redesign may prevent you from doing so.
If you need to use IDisposable objects, the usual way to do this is to create and enter factories that create these IDisposable objects. Thus, the consumer can safely dispose of such an object without any problems.
A common problem is that it is difficult to create objects that implement IDisposable , which are actually thread safe.
If you really want this, you can try creating a decorator that does reference counting. Look for an example from the decorator below. It completes the IService and implements the IService . IService implements IDisposable . The decorator accepts a Func<IService > delegate, which allows you to instantiate. Creating and deleting objects is protected by the lock statement, and the decorator counts the references to it by callers. It will destroy the object and create a new one after the last consumer places the decorator.
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; } }
Note that this implementation is still erroneous for the following reasons:
- Calling
Dispose breaks the decorator several times. - When consumers call
SomeOperation several times (or IService has several methods), the implementation will be broken.
It is difficult to create a decorator that functions as expected. One easy way to do this is to serialize access to the object, but when you do, you probably want to use a single instance for the stream. That would be a lot easier.
Hope this helps.