TICS Coding Standard Viewer 
TIOBE Software Quality Framework
Print-friendly version
©TIOBE Software www.tiobe.com
 
C# Coding Standard
Search

Rule:  5@113Checked automatically with code checker

Synopsis:Implement IDisposable if a class uses unmanaged resources, owns disposable objects or subscribes to other objects
Language:C#
Severity Level:2
Category:Object lifecycle


Description:

A class should implement the IDisposable interface to dispose the managed and unmanaged resources that it owns. A managed resource is a class that implements IDisposable, whereas an unmanaged resource is for instance a System.IntPtr or other value identifying a resource created by unmanaged code.

Implementing IDisposable to dispose resources is especially important for unmanaged resources, because the garbage collector cannot release those resources automatically. Although the garbage collector will eventually release managed resources automatically, it is good practice to dispose claimed resources in a deterministic and eager manner by implementing the Dispose method appropriately.

When implementing the Dispose pattern you should follow certain rules. Some rules depend on whether the class can be inherited from (i.e. it is not sealed) and whether the class holds only managed resources or also holds unmanaged resources. The following cases can be distinguished:

  • Inheritable class holding managed and/or unmanaged resources
  • Derived class holding managed and/or unmanaged resources
  • Sealed class holding only managed resources
  • Sealed class holding both managed and unmanaged resources
  • Sealed class holding managed objects that need to be shared with unmanaged code

The first case can be considered the general case. If possible, prefer to use the Sealed class holding only managed resources because it results in the simplest code. The different cases will be explained next.

Inheritable class holding managed and/or unmanaged resources

Please adhere to the following rules:

  • Implement the method Dispose of the IDisposable interface in one of the following two ways, depending on whether your resource fields are readonly or not:
    • In case of only non-readonly fields:
      public void Dispose()
      {
          Dispose(true);
          GC.SuppressFinalize(this);
      }
      
    • In case of at least one readonly field:
      private bool isDisposed = false;
      
      public void Dispose() 
      {
          if (!isDisposed) 
          {
             Dispose(true);
             isDisposed = true;
             GC.SuppressFinalize(this);
          }
      }
      
  • Implement the method 'protected virtual void Dispose(bool disposing)', in which the resources that this class owns are disposed, as follows:
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose managed resources
        }
        // dispose unmanaged resources
    }
    

    To dispose a managed resource, invoke its Dispose() method inside the 'if (disposing)' guard, for unmanaged resources invoke its resource-specific dispose method outside of the guard.

    The disposing parameter indicates whether the call is done from the Dispose method (true value), which is called deterministically from user code, or from the finalizer (false value), which is called non-deterministically by the garbage collector. The reason that the managed resources should not be disposed when called by the finalizer (false path), is that the managed resources might already have been destroyed by the garbage collector, as the destruction happens in a non-deterministic order.

  • If the class holds any unmanaged resources, implement a finalizer (also known as a 'destructor') as follows:
    ~MyDisposable()
    {
        Dispose(false);
    }
    

    You should avoid creating a finalizer when there are only managed resources, as this complicates garbage collection and causes a needless loss of performance.

  • The pattern prescribes that the Dispose method should be idempotent, such that it is callable multiple times without throwing an exception. Furthermore, subsequent invocations of Dispose should do nothing. In order to achieve this you have two options:

    1. Use a private bool isDisposed = false field, that is set to true when the Dispose method has been called, and do a check on this boolean inside the Dispose() method.
    2. Set a resource field to null (or the default value of the type) after it is disposed, and check for null before disposing. Checking for null and invoking Dispose() can be combined into one line using the '?.' operator.

    The first option should be used when you want to use resource fields that are declared readonly. The advantage of the second option is that the guard and dispose are in close proximity, which aids maintainability, and setting a field to null helps the garbage collector to reclaim memory eagerly.

  • Add an attribute ExternalOwnership to fields holding resources that are not owned by this class, to indicate that it is the responsibility of another class to dispose these resources. You should define this attribute yourself. The containing namespace does not matter.

The following code snippet shows an example of an inheritable (non-sealed) class holding one non-readonly managed resource (connection) and one unmanaged resource (handle):

public class MyDisposable : IDisposable
{
    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject); // just an example 'PInvoke' call for releasing a Win32 resource

    private SqlConnection connection;
    private IntPtr handle;

    public void Dispose()
    {
        // Deterministic call to Dispose(bool)
        Dispose(true); 

        // Prevent the finalizer from being called
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (connection != null)
            {
                connection.Dispose();
                connection = null;
            }
        }

        if (handle != IntPtr.Zero)
        {
            DeleteObject(handle);
            handle = IntPtr.Zero;
        }
    }

    ~MyDisposable()
    {
        // Non-deterministic call to Dispose(bool)
        Dispose(false);
    }
}

The following code snippet shows an example of an inheritable (non-sealed) class holding one readonly managed resource:

public class MyDisposable : IDisposable
{
    private readonly SqlConnection connection;
    private bool isDisposed = false;

    public MyDisposable(string connectionString) 
    {
        connection = new SqlConnection(connectionString);
    }

    public void Dispose()
    {
        if (!isDisposed)
        {
            Dispose(true); 
            isDisposed = true;
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            connection.Dispose();
        }
    }
}
Derived class holding managed and/or unmanaged resources

If a class derives from a class implementing IDisposable, the following rules should be followed:

  • Override the Dispose(bool) method of the base class. In this method you should:
    • Clean up the resources owned by the derived class itself
    • Invoke 'base.Dispose(disposing)'
  • The derived class should not implement IDisposable, nor implement a finalizer

The following is an example implementation of a class deriving from the MyDisposable class:

public class MyDerivedDisposable : MyDisposable
{
    protected SqlConnection anotherConnection;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Dispose or cleanup managed resources of this derived class only
            anotherConnection?.Dispose();
            anotherConnection = null;
        }
        base.Dispose(disposing);
    }
}
Sealed class holding only managed resources

If your class should not be inherited from, you can specify your class as sealed. This simplifies the Dispose pattern considerably:

  • You do not need to implement a 'Dispose(bool)' method, but can dispose resources in the 'Dispose()' method directly
  • You do not need to implement a finalizer, and as a consequence there is no not need to call 'GC.SuppressFinalize(this)'

The following is an example implementation of a sealed class holding only managed resources:

public sealed class SealedManagedResource : IDisposable
{
    private IDisposable connection;

    public void Dispose()
    {
        if (connection != null)
        {
            connection.Dispose();
            connection = null;
        }
    }
}
Alternatively, when using a readonly field:
public sealed class SealedManagedReadonlyResource : IDisposable 
{
    private readonly IDisposable resource;
    private bool isDisposed;
    
    public void Dispose() 
    {
        if (!isDisposed) 
        {
            resource.Dispose();
            isDisposed = true;
        }
    }
}
Sealed classes holding both managed and unmanaged resources

In the scenario where the class is sealed, but also holds unmanaged resources, the code is somewhat more complex.

Example code for this scenario:

public sealed class MyDisposable : IDisposable
{
    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject); // just an example 'PInvoke' call for releasing a Win32 resource

    private IDisposable connection;
    private IntPtr handle;

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

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (connection != null)
            {
                connection.Dispose();
                connection = null;
            }
        }

        if (handle != IntPtr.Zero)
        {
            DeleteObject(handle);
            handle = IntPtr.Zero;
        }
    }

    ~MyDisposable()
    {
        Dispose(false);
    }
}
Sealed class holding managed objects that need to be shared with unmanaged code

When you need to share a managed object with unmanaged code, you need to 'pin' the managed object so that its location on the heap will not be changed by the garbage collector. The managed object will remain pinned during the lifetime of the class implementing IDisposable.

Example code for this scenario:

public sealed class SealedManagedObjectForUnmanagedCode : IDisposable
{
    // The managed object can be any .NET type, including custom ones.
    private object managedObject;
    private GCHandle gcHandle;
    private IntPtr handle;

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

    ~SealedManagedObjectForUnmanagedCode()
    {
        Debug.Assert(!gcHandle.IsAllocated);
        Dispose(false);
    }

    // Method name just an example, can appear anywhere in the class.
    private void CreateResource()
    {
        gcHandle = GCHandle.Alloc(managedObject, GCHandleType.Pinned);
        handle = gcHandle.AddrOfPinnedObject();
    }

    private void Dispose(bool disposing)
    {
        if (gcHandle.IsAllocated)
        {
            gcHandle.Free();
            handle = IntPtr.Zero;
        }
        if (disposing)
        {
            // Cleanup managed resources
        }
    }
}