CHAPTER 13 UNDERSTANDING OBJECT LIFETIME
Rather, when you want to configure your custom C# class types to override the Finalize() method,
you make use of a (C++-like) destructor syntax to achieve the same effect. The reason for this alternative
form of overriding a virtual method is that when the C# compiler processes the finalizer syntax, it
automatically adds a good deal of required infrastructure within the implicitly overridden Finalize()
method (shown in just a moment).
C# finalizers look very similar to constructors in that they are named identically to the class they are
defined within. In addition, finalizers are prefixed with a tilde symbol (~). Unlike a constructor, however,
a finalizer never takes an access modifier (they are implicitly protected), never takes parameters, and
can’t be overloaded (only one finalizer per class).
Following is a custom finalizer for MyResourceWrapper that will issue a system beep when invoked.
Obviously, this example is only for instructional purposes. A real-world finalizer would do nothing more
than free any unmanaged resources and would not interact with other managed objects, even those
referenced by the current object, as you can’t assume they are still alive at the point the garbage collector
invokes your Finalize() method.
// Override System.Object.Finalize() via finalizer syntax.
class MyResourceWrapper
{
~MyResourceWrapper()
{
// Clean up unmanaged resources here.
}
// Beep when destroyed (testing purposes only!)
Console.Beep();
}
If you were to examine this C# destructor using ildasm.exe, you would see that the compiler inserts
some necessary error-checking code. First, the code statements within the scope of your Finalize()
method are placed within a try block (see Chapter 7). The related finally block ensures that your base
classes’ Finalize() method will always execute, regardless of any exceptions encountered within the try
scope.
.method family hidebysig virtual instance void
Finalize() cil managed
{
// Code size
13 (0xd)
.maxstack 1
.try
{
IL_0000: ldc.i4
0x4e20
IL_0005: ldc.i4
0x3e8
IL_000a: call
void [mscorlib]System.Console::Beep(int32, int32)
IL_000f: nop
IL_0010: nop
IL_0011: leave.s
IL_001b
} // end .try
finally
{
IL_0013: ldarg.0
IL_0014:
487