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 release 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 release 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 release 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' as follows:
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
  • Implement the method 'protected virtual void Dispose(bool disposing)' as follows:
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose managed resources
        }
        // dispose unmanaged resources
    }
    

    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.

  • Release all resources that this class owns in the 'Dispose(bool)' method. To release a managed resource, invoke its Dispose() method inside the 'if (disposing)' guard, for unmanaged resources invoke its resource-specific release method outside of the guard.
  • 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 release these resources. You should define this attribute yourself. The containing namespace does not matter.
  • Set a disposed resource field explicitly to null (or the default value of the type).
  • Check a resource field for null (or the default value of the type) before disposing, to prevent releasing the resource twice. Note that checking for null and invoking Dispose() can be combined into one line using the '?.' operator.

The following code snippet shows an example of an inheritable (non-sealed) class holding one 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()
    {
        // Call internal 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()
    {
        Dispose(false);
    }
}
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)
        {
            // Release 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 release 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 MyDisposable : IDisposable
{
    private IDisposable connection;

    public void Dispose()
    {
        if (connection != null)
        {
            connection.Dispose();
            connection = null;
        }
    }
}
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
        }
    }
}