Class instances often encapsulate control over resources that are not managed by the runtime, such as window handles (HWND), database connections, and so on. Therefore, you should provide both an explicit and an implicit way to free those resources. To provide explicit control, implement the Dispose method provided by the IDisposable interface. The consumer of the object should call this method when it is finished using the object.
The garbage collector calls the Finalize method at some point after there are no longer any valid references to the object.
Note that even when you provide explicit control using Dispose, you should provide implicit cleanup using the Finalize method. Finalize provides a backup to prevent resources from permanently leaking if the programmer fails to call Dispose.
Finalize
The following rules outline the usage guidelines for the Finalize method:
- Implement Finalize only on objects that require finalization. There are performance costs associated with Finalize methods.
- If you require a Finalize method, consider implementing IDisposable to allow users of your class to avoid the cost of invoking the Finalize method.
- Do not make the Finalize method more visible. It should be protected, not public.
- An object’s Finalize method should free any external resources that the object owns. Moreover, a Finalize method should release only resources that the object has held onto. The Finalize method should not reference any other objects.
- Do not directly call a Finalize method on an object other than the object’s base class. This is not a valid operation in the C# programming language.
- Call the base class’s Finalize method from an object’s Finalize method.
The following example illustrates the basic design pattern for implementing Dispose.
// Design pattern for a base class. public class Base: IDisposable { private bool disposed = false; //Implement IDisposable. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // No need to call finalize anymore. All resources are freed now. } // If disposing argument is true, managed and unmanaged resources will be released, // otherwise only unmanaged resources will be released. protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { // Here free only managed objects. // ... } // Here free only unmanaged objects. // Set large fields to null. //... disposed = true; } } // Use C# destructor syntax for finalization code. protected ~Base() { // Simply call Dispose(false). Dispose(false); } } // Design pattern for a derived class. public class Derived: Base { private bool disposed = false; protected override void Dispose(bool disposing) { if (!disposed) { if (disposing) { // Release managed resources. } // Release unmanaged resources. // Set large fields to null. // Call Dispose on your base class. disposed = true; } base.Dispose(disposing); } // The derived class does not have a Finalize method // or a Dispose method without parameters because it inherits // them from the base class. }
In the Finalize method you should never reference managed objects, since these may no longer be accessible, then call Dispose(false) so that only unmanaged objects are freed.
In the public Dispose() method we should invoke the SuppressFinalize() method of the Garbage Collector (GC) since already all objects (managed and unmanaged) were released.