views:

146

answers:

4

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:

  1. Make IJob : IDisposable, forcing all jobs to implement a Dispose() method. This will allow me to work with jobs in using blocks, but as most jobs are not expected to need explicit disposal, this might add unneeded confusion for the client developers.

  2. Do all work involving jobs in try-finally blocks, and use finally to ensure that Dispose() is called if the job implements IDisposable. This makes it easier for clients to implement a new job class - by not having to implement an empty Dispose() 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.

+2  A: 

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 ;-)

James Keesey
The OP is already disposing owned objects in both options, so I don't see how this is an argument for option #2.
Wim Coenen
+8  A: 

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.

Henk Holterman
Good point. However, most streams will need disposal, and I expect a only minority of jobs to need that.
Jørn Schou-Rode
It occurs to me that `System.Web.IHttpModule` also requires its implementors to provide a `Dispose()` method, even though only few modules (IMHO) needs it. I guess this provides additional precedence for #1.
Jørn Schou-Rode
+1, I think moreover, it makes sense to provide a standardized method for a Job to clean up what it used. By using `IDisposable` you get to benefit from the framework support and end-users benefit from at least having to think about where to place appropriate cleanup code.
sixlettervariables
+1 the second option also has a disadvantage that it's easy to "lose" IDisposable in an adapter. Having adapters and querying for interfaces does not mix well. Making things more explicit makes maintenance easier.
Wim Coenen
+3  A: 

I think of it this way. I'd rather a developer implement an empty Dispose method than forget to implement a necessary Dispose method.

Stan R.
+5  A: 

#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();
        }
    }
}
Bryan Watts
+1, I like your argument about semantics, but I think I will go with the first solution, for the reasons mentioned in the other answers and the question itself.
Jørn Schou-Rode