I am working on a .NET project, which needs to interact with some user defined classes - reffered to as "jobs". All job classes must implement a 开发者_开发技巧specific interface IJob
in order order for the library to consume them. Sometimes a job class might hold unmanaged resource, which needs to be explicitly disposed.
How should I ensure that all jobs are properly disposed after use, if I do not know in advance if the job needs explicit disposal? I have a few ideas myself, but would like to hear your comments/suggestions:
Make
IJob : IDisposable
, forcing all jobs to implement aDispose()
method. This will allow me to work with jobs inusing
blocks, but as most jobs are not expected to need explicit disposal, this might add unneeded confusion for the client developers.Do all work involving jobs in
try-finally
blocks, and usefinally
to ensure thatDispose()
is called if the job implementsIDisposable
. This makes it easier for clients to implement a new job class - by not having to implement an emptyDispose()
method - but it also hides the fact that the library knows and cares about disposable jobs.
After writing this up, I tend to lean towards solution #1, but I still think it would be nice to see alternative solutions, and additional pros/cons to the two I already have in mind.
There is a precedent: The Stream base class is IDisposable nad therefore all Streams descendants are. But MemoryStream doesn't need Disposing.
But don't aim for try/finally, the using() { }
block is a more convenient shorthand.
So your choice is: Do you want all Jobs to be IDisposable or just some?
The first option incurs a small overhead, the second one makes it easier to forget Dispose (using) when it is necessary.
#2 is how the foreach
construct works. It is also how Autofac's container disposal works.
The semantic difference is whether you say that a job itself is disposable, or whether an implementation might be disposable.
It is clear from your example that the former is not true, that jobs are not inherently disposable. Therefore, I recommend #2, but with an extension method to centralize the try/finally
:
public static void Execute(this IJob job)
{
try
{
job.Run();
}
finally
{
var disposableJob = job as IDisposable;
if(disposableJob != null)
{
disposableJob.Dispose();
}
}
}
I think of it this way. I'd rather a developer implement an empty Dispose
method than forget to implement a necessary Dispose
method.
I would go with #2 and document that any disposable object will be disposed. Basically if you take ownership of an object you are obligated to dispose of objects that implement IDisposable.
If you read Effective C#/ More Effective C#, Bill Wagner give the same advice (which I agree with obviously ;-)
精彩评论