问题
What is the best way to properly deal with a default implementation of a interface when the interface doesn't inherit from IDisposable? For example, suppose I want to do
public class FooGetter : IDisposable {
private IFooProvider fooProvider = MyContainer.GetDefault<IFooProvider>();
...
public void Dispose(){
...
if (fooProvider != null) fooProvider.Dispose(); // obviously has compile error here
}
}
And it just so happens that the default implementation of IFooProvider is IDisposable, but IFooProvider interface does not inherit from IDisposable. How/where am I supposed to dispose of it?
The question isn't just for dependency injection containers; it would also apply to a tightly-coupled dependency:
private IFooProvider fooProvider = new PatrickProvider();
In this case, I could keep another reference so that I can later Dispose() it, but that seems really janky:
private PatrickProvider defaultFooProvider = new PatrickProvider();
private IFooProvider fooProvider = defaultFooProvider;
Looking for best (or good) practices here.
回答1:
Best practices when using a DI container is to let your DI container handle the lifetime of the object for you. This is not possible if you use your DI container as a service locator (boo! anti-pattern!!), like you seem to be doing with MyContainer.GetDefault, but if you properly have your dependencies injected through constructor injection, then it would work well.
For tightly-coupled dependencies, you would have to do something stupid like
using(fooProvider as IDisposable)
{
// Code that uses fooProvider
}
I think (but am not 100% sure) that using will do nothing if passed null, so this will work whether or not fooProvider implements IDisposable.
There is a good discussion of IDisposable as it relates to dependencies an DI in Mark Seemann's Dependency Injection in .NET chapter 8.
回答2:
You could do a runtime check that the object implements IDisposable and if so, dispose of it:
public class FooGetter : IDisposable
{
private IFooProvider fooProvider = MyContainer.GetDefault<IFooProvider>();
...
public void Dispose()
{
IDisposable disposable = fooProvider as IDisposable;
if (disposable != null)
{
fooProvider.Dispose();
}
}
}
You still need to have an inkling that the implementation of your interface may be disposable otherwise the code is a bit pointless, but for your case it will ensure that Dispose() is called on implementations that have it.
Ideally, your interface should derive from IDisposable to state that part of its contract is that you must dispose of it when finished, but I acknowledge that in reality this may not always be possible.
回答3:
To me it seems that there are a few things to consider.
First, it is perfectly reasonable to imagine an interface IFoo where some implementations might have resources to dispose of or cleanup to perform while others might not; therefore, it is some consider it reasonable not to make IFoo inherit IDisposable.
public interface IFoo{
// ...
}
// Has no cleanup or resources
public interface Foo1:IFoo{
// ...
}
// Does have resources to release and cleanup to perform
public interface Foo2:IFoo,IDisposable{
// ...
}
See this answer and this discussion for views of whether the choice to implement IDisposable should be up to concrete classes or up to abstract classes / interfaces. EDIT: Struck most of this paragraph because after reviewing the discussions, I see good points on the "have the interface define it" side and will be reevaluating my opinion. Plus, that decision isn't really relevant to the question being asked.
Now all that aside, if you have IFoo x, and some implementations implement IDisposable while others do not, you can check whether x implements IDisposable. In the case that it does, properly dispose of it. If not, then you should trust that whoever wrote x's implementation of IFooProvider did not implement IDisposable because you need not call it, and be secure in the knowledge that you've done all you can.
IFoo x;
//x = whatever
if (x is IDisposable)
{
((IDisposable)x).Dispose();
}
回答4:
If you're defining IFooProvider, why not make it inherit IDispoable? If someone comes up with an implementation which doesn't need cleanup, it can simply implement an empty Dispose method.
来源:https://stackoverflow.com/questions/5890154/idisposable-implementation-of-a-non-idisposable-interface