Tuesday, August 31, 2010

Correctly disposing of objects - implement IDisposable

To continue with our FxCop backlog, we are going to look at a couple rules dealing with IDisposable. Consider the following class:

  public class TimedProcessor
  {
      Timer startTimer = new Timer();
 
      public TimedProcessor(double interval)
      {
          startTimer = new Timer(interval);
          startTimer.Elapsed += timer_Elapsed;
      }
 
      void timer_Elapsed(object sender, ElapsedEventArgs e)
      {
          Console.WriteLine("Do work here");
      }
  }

Running FxCop on this will report a violation of the rule Types that own disposable fields should be disposable. The problem is that the startTimer field is of type Timer, which implements IDisposable. To properly use the timer, we need to call its Dispose method as soon as we are done with it. The solution is to implement IDisposable on our class and make sure we call startTimer.Dispose().

The class after fixing the violation:

  public class TimedProcessor : IDisposable
  {
      Timer startTimer = new Timer();
 
      public TimedProcessor(double interval)
      {
          startTimer = new Timer(interval);
          startTimer.Elapsed += timer_Elapsed;
      }
 
      void timer_Elapsed(object sender, ElapsedEventArgs e)
      {
          Console.WriteLine("Do work here");
      }
 
      public void Dispose()
      {
          if (startTimer != null)
              startTimer.Dispose();
      }
  }

Note that I check for null before calling Dispose. If an error elsewhere left the object in an invalid state, we don't want our Dispose method to throw a NullReferenceException.

Now, say we add a second field called endTimer. Running FxCop now will report a violation of the rule Disposable fields should be disposed. In this case we have already implemented IDisposable, but not all of our disposable fields have been addressed. To fix this, we need to modify our Dispose method slightly:

  public void Dispose()
  {
      if (startTimer != null)
          startTimer.Dispose();
 
      if (endTimer != null)
          endTimer.Dispose();
  }