Saturday, July 18, 2015

C# IDisposable best practice

General Information

  • .net managed code talks to and uses external resources, that needs to implement IDisposable interface
  • GC(Garbage Collector) tries best to clean memory when using external resources, such as SQLconnection, without IDisposable. Thus, it is impossible to detect when app will fail.
  • GC is triggered when the memory threshold is reached.

Managed resources v.s. unmanaged resources


Managed resources basically means "managed memory" that is managed by the garbage collector. When you no longer have any references to a managed object (which uses managed memory), the garbage collector will (eventually) release that memory for you.

Unmanaged resources are then everything that the garbage collector does not know about. For example:
Open files
Open network connections
Unmanaged memory
In XNA: vertex buffers, index buffers, textures, etc.

Normally you want to release those unmanaged resources before you lose all the references you have to the object managing them. You do this by calling Dispose on that object, or (in C#) using the using statement which will handle calling Dispose for you.

If you neglect to Dispose of your unmanaged resources correctly, the garbage collector will eventually handle it for you when the object containing that resource is garbage collected (this is "finalization"). But because the garbage collector doesn't know about the unmanaged resources, it can't tell how badly it needs to release them - so it's possible for your program to perform poorly or run out of resources entirely.

If you implement a class yourself that handles unmanaged resources, it is up to you to implement Dispose and Finalize correctly.

Best Practices

  • best practice #1: Dispose of IDisposable objects as soon as you can. 
  • Implementing IDisposable code pattern as:
  •  private bool _disposed;   
      public void Dispose()    
      {    
       Dispose(true);    
       // Use SupressFinalize in case a subclass    
       // of this type implements a finalizer.    
       GC.SuppressFinalize(this);    
      }    
      protected virtual void Dispose(bool disposing)    
      {    
       if (!_disposed)    
       {    
       if (disposing)    
       {    
        // Clear all property values that maybe have been set    
        // when the class was instantiated    
        id = 0;    
        name = String.Empty;    
        pass = String.Empty;    
       }    
       // Indicate that the instance has been disposed.    
       _disposed = true;    
       }    
      }    
    
  • best practice #2: If you use IDisposable objects (e.g. SQLConnection _connection) as instance fields, implement IDisposable.
  • best practice #3: allow Dispose() to be called multiple times and do not throw exceptions (last 3 lines in the code sample below)
  •  public class DatabaseState : IDisposable   
      {   
       private SqlConnection _connection;   
      public void Dispose()   
      {   
       Dispose(true);   
       GC.SuppressFinalize(this);   
      }   
      protected virtual void Dispose(bool disposing)   
      {   
       if(disposing && _connection != null)   
        { _connection.Dispose();   
        _connection = null;   
        }  
      }   
  • best practice #4 implement IDisposable to support disposing resources in a class heraychy.
  • best practice #5 if you use unmanaged resources, declare a finalizer which cleans them up.
  • best practice #6: Visual Studio using code analysis CA2000 (not enabled by default) to check this issue.
  • best practice #7: if your class implements an interface and use IDisposable fields, extend the interface from IDisposable rather than the implementing class. This conflicts with OO Design.
  • best practice #8: if you implement IDisposable, do not implement it explicitly.

No comments:

Post a Comment