Important Notes
- Each class has one and only one finalizer.
- The name of the finalizer is always Finalize.
- You cannot call a finalizer, nor can you overload it.
- You cannot predict with certainty when it will be called.
Working WinForms Example
The following demonstrates using a finalizer and System.GC.Collect(). I placed a MessageBox.Show() in the finalizer so you can see when it is called. The finalizer gets called when the compiler needs to free resources or you call System.GC.Collect(). You'll notice that although the local variable in the button click event falls out of scope, your finalizer is not called until either you close the form or click the button that calls System.GC.Collect().
Create a form with two buttons and add code as follows:
namespace CR_Constructor;
interface
uses
System.Drawing,
System.Collections,
System.Collections.Generic,
System.Linq,
System.Windows.Forms,
System.ComponentModel;
type
/// <summary>
/// Summary description for MainForm.
/// </summary>
MainForm = partial class(System.Windows.Forms.Form)
private
method button1_Click(sender: System.Object; e: System.EventArgs);
method button2_Click(sender: System.Object; e: System.EventArgs);
protected
method Dispose(disposing: Boolean); override;
public
constructor;
end;
Cyborg = class(System.Object)
private
public
property CyborgName: String; //Property using implicit syntax.
constructor();
constructor(pName: String);
//This is the full version of a finalizer.
finalizer Finalize;
//As with constructors, you could have just used the keyword.
//finalizer;
end;
implementation
{$REGION Construction and Disposition}
constructor MainForm;
begin
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
end;
method MainForm.Dispose(disposing: Boolean);
begin
if disposing then begin
if assigned(components) then
components.Dispose();
//
// TODO: Add custom disposition code here
//
end;
inherited Dispose(disposing);
end;
{$ENDREGION}
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
var
MyRobot1: Cyborg;
MyRobot2: Cyborg;
begin
MyRobot1 := New Cyborg;
MyRobot1.CyborgName := "John";
MessageBox.Show("My robot's name is " + MyRobot1.CyborgName + ".");
MyRobot2 := New Cyborg("Cameron");
MessageBox.Show("My robot's name is " + MyRobot2.CyborgName + ".");
end;
method MainForm.button2_Click(sender: System.Object; e: System.EventArgs);
begin
System.GC.Collect;
end;
constructor Cyborg();
begin
end;
constructor Cyborg(pName: String);
begin
CyborgName := pName;
end;
finalizer Cyborg.Finalize;
begin
MessageBox.Show("In finalizer.");
end;
end.
Implementing IDisposable
If you wish to have control over freeing the objects you create (either managed or unmanaged object instances), use the IDisposable interface and add it to your class and implement according to the standard IDisposable pattern.
Add IDisposable to Your Class
//
//Interface
//
//1. Add IDisposable interface to inheritance syntax
// and add two Dispose methods and a member field boolean.
Cyborg = class(System.Object, IDisposable)
private
disposed: Boolean;
method Dispose(disposing: Boolean);
public
finalizer Finalize;
method Dispose;
end;
//
//Implementation
//
//2. Implement the two dispose methods.
method Cyborg.Dispose;
begin
Dispose(true);
GC.SuppressFinalize(self);
end;
method Cyborg.Dispose(disposing: Boolean);
begin
if not disposed then
begin
if disposing then
begin
// Dispose of managed resources.
end;
// Dispose of unmanaged resources
disposed := true;
end
end;
//3. Set your boolean to false in finalizer
// using your Dispose() method.
finalizer Cyborg.Finalize;
begin
Dispose(false);
end;
Using IDisposable
Once implemented you can free object instances by calling Dispose.
var MyRobot1: Cyborg;
MyRobot1 := New Cyborg;
MyRobot1.CyborgName := "John";
MessageBox.Show("My robot's name is " + MyRobot1.CyborgName + ".");
MyRobot1.Dispose;
Working IDisposable Example
In the following example, we implement the IDisposable interface in our Cyborg class from above and call Dispose in the button's click event. You'll notice that whether you click our second button that calls System.GC.Collect or exit the form, the dialog in our finalizer is never displayed because the managed object instances that were used are already disposed of properly.
namespace CR_Constructor;
interface
uses
System.Drawing,
System.Collections,
System.Collections.Generic,
System.Linq,
System.Windows.Forms,
System.ComponentModel;
type
/// <summary>
/// Summary description for MainForm.
/// </summary>
MainForm = partial class(System.Windows.Forms.Form)
private
method button1_Click(sender: System.Object; e: System.EventArgs);
method button2_Click(sender: System.Object; e: System.EventArgs);
protected
method Dispose(disposing: Boolean); override;
public
constructor;
end;
Cyborg = class(System.Object, IDisposable)
private
disposed: Boolean;
method Dispose(disposing: Boolean);
public
property CyborgName: String;
constructor;
constructor(pName: String);
finalizer Finalize;
method Dispose;
end;
implementation
{$REGION Construction and Disposition}
constructor MainForm;
begin
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
end;
method MainForm.Dispose(disposing: Boolean);
begin
if disposing then begin
if assigned(components) then
components.Dispose();
//
// TODO: Add custom disposition code here
//
end;
inherited Dispose(disposing);
end;
{$ENDREGION}
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
begin
var MyRobot1: Cyborg;
var MyRobot2: Cyborg;
MyRobot1 := New Cyborg;
MyRobot1.CyborgName := "John";
MessageBox.Show("My robot's name is " + MyRobot1.CyborgName + ".");
MyRobot2 := New Cyborg("Cameron");
MessageBox.Show("My robot's name is " + MyRobot2.CyborgName + ".");
MyRobot1.Dispose;
MyRobot2.Dispose;
end;
method MainForm.button2_Click(sender: System.Object; e: System.EventArgs);
begin
System.GC.Collect;
end;
constructor Cyborg();
begin
end;
constructor Cyborg(pName: String);
begin
CyborgName := pName;
end;
finalizer Cyborg.Finalize;
begin
Dispose(false);
//This dialog never shows demonstrating the
//finalizer is not called by the garbage collector.
MessageBox.Show("In finalizer");
end;
method Cyborg.Dispose;
begin
Dispose(true);
GC.SuppressFinalize(self);
MessageBox.Show("In Dispose");
end;
method Cyborg.Dispose(disposing: Boolean);
begin
if not disposed then
begin
if disposing then
begin
// Dispose of managed resources.
end;
// Dispose of unmanaged resources
disposed := true;
end
end;
end.
Q. If I implement IDisposable, can I also let the garbage collector free my objects?
A. It depends on how you implemented IDisposable and how you are using resources. In general, if you implement IDisposable you should take ownership of calling Dispose for each object instance you create. However, in the simple Working IDisposable Example in this article, you can either call Dispose to free the objects or let the garbage collector free them as demonstrated by the button click events.
Q. If I consume an object that implements IDisposable, should I always call Dispose?
A. In general, yes if it's a shared resource. However, again it depends on why IDisposable was implemented. If Dispose is freeing a resource that may be needed, like a file handle, then yes. There may be exceptions to this rule, but if an object implements IDisposable and the resource is a shared resource, then yes when you create an object instance, you should take ownership and also call Dispose at the appropriate time.