Sunday, June 01, 2014

Please do not implement a finalizer willy-nilly!

I see many people implement the finalizer when they implement the dispose pattern in c#.

class MyDisposableClass : IDisposable
{
   public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);          
   }

   protected virtual void Dispose()
   {
   }

   ~MyDisposableClass()
   {
      Dispose();
   }
}

Please don’t do this (implement the finalizer)  unless your class uses unmanaged resources! One reason for this is that if you access managed resources in your Dispose method, they may have already been garbage collected if the Dispose method was called via the finalizer!

Here are some things to consider: (all pulled from MSDN documentation)

  1. The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects. (http://msdn.microsoft.com/en-us/library/fs2xkftw(v=vs.110).aspx)
  2. Finalizers are notoriously difficult to implement correctly, primarily because you cannot make certain (normally valid) assumptions about the state of the system during their execution. (http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx)
  3. If a type does override the Finalize method, the garbage collector adds an entry for each instance of the type to an internal structure called the finalization queue. The finalization queue contains entries for all the objects in the managed heap whose finalization code must run before the garbage collector can reclaim their memory. The garbage collector then calls the Finalize method automatically…. (http://msdn.microsoft.com/en-us/library/system.object.finalize(v=vs.110).aspx)

Remember: Finalizers are typically not needed and even when needed there are better patterns available that allow you to side-step implementing the finalizer (eg: SafeHandle).

Note:

  1. Even if you implement the Finalizer “correctly” such that when you call Dispose, it only cleans out unmanaged resources, if you don’t have unmanaged resources, you have introduced a tiny performance penalty into your app and allow someone else to introduce bugs. So if you see a finalizer, check to see if you REALLY need it and if you don’t, get rid of it (and maybe even the implementation of the IDisposable interface.
  2. Sometimes the Dispose pattern is implemented to free up managed memory being used by the class. Again if you have written your code well, you should be able to rely on the .Net garbage collector to free up the memory for you.

Correctly implemented Dispose pattern

class CorrectlyImplementedDisposableClass: IDisposable
{
   bool disposed = false;

   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.  }

      // Only Free unmanaged objects here.  disposed = true;
   }
}
 
class CorrectlyImplementedFinalizedClass: CorrectlyImplementedDisposableClass
{
   ~CorrectlyImplementedDisposableClass()
   {
      Dispose(false);
   }
}

No comments:

Post a Comment

Remember, if you want me to respond to your comment, then you need to use a Google/OpenID account to leave the comment.