问题
I have a class that use constructor injections.
public class MyClass
{
public MyClass(IInterface1 interface1)
{
}
public Dispose()
{
interface1.dispose();
}
}
interface1 will be injected by DI. But sometimes, i need to create MyClass manually.
public class MyOtherClass
{
private readonly IInterface1 _interface1;
public MyOtherClass()
{
_interface1 = new Interface1();
}
public Foo()
{
foo = new MyClass(_interface1);
bar = new MyClass(_interface1);
}
}
in my dispose method, interface1 is always disposed when MyClass is destroyed. The problem is, interface1 is own by MyOtherClass and might still be used by other instances and shouldn't be disposed. How can i resolve this?
回答1:
You should not call
interface1.dispose();
inside MyClass.
If interface1 is created by the DI container, it will be rleased there. If you are creating it explicitly as in MyOtherClass, Dispose it in MyOtherClass.
回答2:
There are two problems with your code.
First of all, MyClass
'illegally' takes the ownership of the IInterface1
, while it has no idea what the lifetime of that instances is. This means that if IInterface1
is reused, the system breaks.
The basic rule of ownership is that "he who creates an instance is responsible of disposing it" (the RAII idiom). Since MyClass
didn't create IInterface1
, he should not dispose it. MyClass
should therefore not implement a Dispose
method and not call IInterface1.Dispose()
.
Second, by letting the IInterface1
implement IDisposable
, your code violates the Dependency Inversion Principle (DIP), because the DIP states:
Abstractions should not depend on details. Details should depend on abstractions.
Your IInterface1
however, depends on an implementation detail, because whether or not some component has unmanaged resources that needs to be disposed is an implementation detail. It is very unlikely that every implementation of that IInterface1
will always need to dispose resources and because of that your interface leaks implementation details of a specific implementation.
So instead of letting the IInterface1
implement IDisposable
you simply let the given implementation implement IDisposable
. What's nice about this is that it minimized IInterface1
's API, which makes it easier to work with and possibly allows you to conform to the Interface Segregation Principle.
If you do that, the problem goes away immediately, since MyClass
has no clue whether or not IInterface1
can be disposed or not (which is good), which means it can't accidentally call Dispose
in the first place.
This of course leaves the disposal of that instance up to the part of the system that created that instance (which is good). If this instance is created on your behalf by a DI library (if you use one), the DI library is usually responsible of disposing that instance. If you don't use a container, you will have to ensure disposal yourself obviously.
Do note that not all containers track all instances. For instance, Unity and Simple Injector do not track (and dispose) transient instances automatically. In most cases however, disposable components should be registered with a scoped lifestyle (per web request or something similar). I think in that case all containers dispose instances that are registered with such lifestyle.
来源:https://stackoverflow.com/questions/26753237/how-to-make-sure-not-to-dispose-objects-that-are-still-used-by-others