If you read the Delphi newsgroups, you’ve probably seen my warnings against using finalizers as a matter of course in your .NET code. If you were not satisified with my (slightly vague) reasons, go read Chris Brumme’s article on Finalization for the exhaustive list of sins of Finalize.
Note that while C#’s “destructor” syntax implements a finalizer, Delphi’s destructor syntax implements the IDisposable pattern, not a finalizer. The main hazard to IDisposable is that there are no guarantees that it will be called, but as Chris points out, finalizers provide no guarantees either, and carry considerably higher costs.
Also, VCL for .NET already implements something similar to Whidbey’s SafeHandle. TResHandleWrapper and THWndWrapper hold onto a Win32 handle and implement finalization semantics, thus relieving the larger control classes of responsibility for finalization headaches and minimizing the amount of memory held hostage in the finalization queues. Granted, SafeHandle will do even more for us in the Whidbey release of .NET sometime next year, but encapsulating the handles in lightweight classes does quite well for us in VCL for .NET right now. When Whidbey is available, we’ll update VCL for .NET to use SafeHandles, probably by reparenting or aliasing the existing TResHandleWrapper and other classes.
.NET’s lack of deterministic finalization has been the root of a number of major headaches in implementing Delphi language semantics on top of the .NET platform. We implemented unit initialization order using .cctor type initializers, but for unit finalization sections there was little hope because the platform simply doesn’t support orderly shutdown. If you are compiling a stand-alone program .exe with Delphi, then the unit finalization sections will be called by compiler-generated code that follows the main procedure body. If your program exits abnormally, or your code is built as an assembly, your unit finalization code might not execute at all.
The only good news in all this is that 90% of the unit finalization code in existing Delphi VCL applications does little more than free allocated object instances or release resources. In .NET, you don’t need to explicitly free object instances, and you should use lightweight classes like TResHandleWrapper to encapsulate your resource handles.
The other 10% of finalization cases are typically unregistering classes from globally managed lists. This is required in Win32 code to make sure that your component types registered for design-time use are removed from the registration lists when your package is removed from memory at design time. This unregistration is not necessary in .NET because a) metadata reflection eliminates the need to register types into global lists and b) to be able to unload an assembly at runtime requires that the assembly be loaded into an appdomain, and global data is never shared between appdomains, so there is no need to remove your registration from the global list because the global list will be disappearing with your appdomain as well. (clear as mud? good.)
Anyway, go read Brumme’s article. If that doesn’t dissuade you from using finalizers, nothing will.