In general, using Finalize
to clean up an outside managed resource is useless and may be dangerous. When an object finalizer runs, managed resources it holds are likely either:
- Cleaned up already as a consequence of having had their finalizers run, in which case disposing them will be useless.
- Not yet cleaned up, but recognized as being unreferenced by any live objects, in which case their finalizer will run in due course whether or not the currently-executing finalizer tries to clean them up.
- Still in use by other live objects, in which case they should not be cleaned up by the presently-running finalizer.
There are some situations where it may be helpful to have finalizers call cleanup code on other managed objects, but such invocation is generally only appropriate in cases where objects know about each others' internal details, and should generally be performed either using protected
or internal
methods, or using privately-exchanged delegates. In some cases, for example, there may be two or more objects which know about each other, and have finalizers that must run in a particular order.
As an example, there may be one class whose purpose is to send data to a USB device, and another class whose purpose is to control a piece of equipment connected to that device. If the latter class wants to make certain the equipment receives a "shutdown" command, it may be necessary that its finalizer send the command before the USB-connection class finalizer closes the connection. The cleanest way to handle this may be to have the constructor of the USB-connection object accept a callback which it will invoke from its finalizer. If the class has no such feature, things can be rather icky. Another approach may be to have an object separate from the wrapper which holds all the information necessary for cleanup, along with a WeakReference
to the wrapper object and has a timer-tick event which will perform cleanup if the WeakReference
is dead. If the timer is bound to the thread on which the wrapper object is created, that may allow cleanup to be performed even if the wrapper object is abandoned, provided the thread is still alive (if it may not be, things get more complicated).