I’ve written a class which pairs up a TransactionScope with an Linq to Sql DataContext.
It implements the same methods as the TransactionScope, Dispose() and Complete() and exposes the DataContext.
It’s purpose is to ensure that DataContexts are not re-used, they are paired up with a single transaction and Disposed along with it.
Should I include a Finalize method in the class? One that calls Dispose if it has not already been called? Or it that only for IDisposables that reference unmanaged resources?
No, never implement a finaliser in a class that is disposable just because it wraps a disposable class.
Consider that you have three clean-up scenarios for a class with
Disposeand a finaliser:Dispose()is called.Dispose(), but note that you should always suppress your finaliser when anything puts it in a state where it doesn’t need to be cleaned up, and re-registered if it is put in a state where it does need it – e.g. if you had anOpen()/Close()pair of methods).Now, if you are directly managing an unmanaged resource (e.g. a handle through an
IntPtr), these three of these scenarios where you will have one of the two clean-up methods called directly match the three scenarios where you need clean-up to happen.Okay. So, let’s consider a disposable wrapping a disposable where the “outer” class has a finaliser implemented correctly:
The finaliser doesn’t do anything, because there’s no unmanaged clean-up for it to handle. The only effect is that if this null finaliser hasn’t been suppressed, then upon garbage collection it will be put in the finaliser queue – keeping it and anything only reachable through it’s fields alive and promoting them to the next generation – and eventually the finalisation thread will call this nop method, mark it as having been finalised and it becomes eligible for garbage collection again. But since it was promoted it’ll be Gen 1 if it had been Gen 0, and Gen 2 if it had been Gen 1.
The actual object that did need to be finalised will also be promoted, and it’ll have to wait that bit longer not just for collection, but also for finalisation. It’s going to end up in Gen 2 no matter what.
Okay, that’s bad enough, let’s say we actually put some code in the finaliser that did something with the field that holds the finalisable class.
Wait. What are we going to do? We can’t call a finaliser directly, so we dispose it. Oh wait, are we sure that for this class the behaviour of
Dispose()and that of the finaliser is close enough that it’s safe? How do we know it doesn’t hold onto some resources via weak-references that it will try to deal with in the dispose method and not in the finaliser? The author of that class knows all about the perils of dealing with weak-references from within a finaliser, but they thought they were writing aDispose()method, not part of someone else’s finaliser method.And then, what if the outer finaliser was called during application shut-down. How do you know the inner finaliser wasn’t already called? When the author of the class was writing their
Dispose()method, are they sure to ponder “okay, now let’s make sure I handle the case of this being called after the finaliser has already run and the only thing left for this object to do is have it’s memory freed?” Not really. It might be that they guarded against repeated calls toDispose()in such a way that also protects from this scenario, but you can’t really bet on it (especially since they won’t be helping that in the finaliser which they know will be the last method ever called and any other sort of cleanup like nulling fields that won’t be used again to flag them as such, is pointless). It could end up doing something like dropping the reference count of some reference-counted resource, or otherwise violating the contract of the unmanaged code it is its job to deal with.So. Best case scenario with a finaliser in such a class is that you damage the efficiency of garbage collection, and worse-case is you have a bug which interfere with the perfectly good clean-up code you were trying to help.
Note also the logic behind the pattern MS used to promote (and still have in some of their classes) where you have a protected
Dispose(bool disposing)method. Now, I have a lot of bad things to say about this pattern, but when you look at it, it is designed to deal with the very fact that what you clean up withDispose()and what you clean up in a finaliser are not the same – the pattern means that an object’s directly-held unmanaged resources will be cleaned up in both cases (in the scenario of your question, there are no such resources) and that managed resources such as an internally-heldIDisposableobject are cleaned-up only fromDispose(), and not from a finaliser.Implement
IDisposable.Dispose()if you have anything that needs to be cleaned up, whether an unmanaged resource, an object that isIDisposableor anything else.Write a finaliser if, and only if you directly have an unmanaged resource that needs to be cleaned up, and make cleaning it up the only thing you do there.
For bonus points, avoid being in both classes at once – wrap all unmanaged resources in disposable and finalisable classes that only deal with that unmanaged classes, and if you need to combine that functionality with other resources do it in a disposable-only class that uses such classes. That way clean-up will be clearer, simpler, less prone to bugs, and less damaging to GC efficiency (no risk of finalisation of one object delaying that of another).