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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace CR_Constructor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Create and use object instance.
Cyborg MyRobot = new Cyborg("Cameron");
MessageBox.Show("My robot's name is " + MyRobot.CyborgName);
}
private void button2_Click(object sender, EventArgs e)
{
//Explicitly call garbage collector (never required for managed code).
System.GC.Collect();
}
}
public class Cyborg: Object
{
public string CyborgName;
//Constructor.
public Cyborg(string pName)
{
CyborgName = pName;
}
//Finalizer.
~Cyborg()
{
MessageBox.Show("Free non-managed resources here.");
}
}
}
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
public class Cyborg: Object, IDisposable
{
private bool disposed = false;
// Do not make this method virtual.
// Don't let a descendant class override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Call GC.SupressFinalize to remove from GC queue.
GC.SuppressFinalize(this);
}
// Dispose of managed and/or unmanaged resources.
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
//Free managed resources here.
}
// Free unmanaged resources here.
//Indicated disposing completed.
disposed = true;
}
}
//Finalizer.
~Cyborg()
{
Dispose(false);
}
}
Using IDisposable
Once implemented you can free object instances by calling Dispose.
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. In this implementation, if you forget to call Dispose, the GC will clean up the object at an appropriate time and call your finalizer.
Setup form:
After you implement the code below, perform the following use cases:
- Click the Instantiate Object Only button and close the form. The GC cleans up upon close of the form.
- Click the Instantiate Objects Only button then click the GC.Collect button. The GC cleans up at that time.
- Click the Instantiate and Dispose button. Notice the objects are disposed of in the click event. Then close the form and notice your finalizer code is never executed.
Complete Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace CR_Constructor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Cyborg MyRobot1 = new Cyborg();
MyRobot1.CyborgName = "John";
MessageBox.Show("My robot's name is " + MyRobot1.CyborgName);
Cyborg MyRobot2 = new Cyborg("Cameron");
MessageBox.Show("My robot's name is " + MyRobot2.CyborgName);
}
private void button2_Click(object sender, EventArgs e)
{
System.GC.Collect();
}
private void button3_Click(object sender, EventArgs e)
{
Cyborg MyRobot1 = new Cyborg();
MyRobot1.CyborgName = "John";
MessageBox.Show("My robot's name is " + MyRobot1.CyborgName);
Cyborg MyRobot2 = new Cyborg("Cameron");
MessageBox.Show("My robot's name is " + MyRobot2.CyborgName);
MyRobot1.Dispose();
MyRobot2.Dispose();
}
}
public class Cyborg: Object, IDisposable
{
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
MessageBox.Show("Dispose executed.");
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
//Free managed resources here.
}
// Free unmanaged resources here.
disposed = true;
}
}
public string CyborgName;
public Cyborg() { }
public Cyborg(string pName)
{
CyborgName = pName;
}
~Cyborg()
{
MessageBox.Show("If we call Dispose, does this execute? No.");
Dispose(false);
}
}
}
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.