Implement a Dispose method

Implement a Dispose method

In this article

Implementing the Dispose method is primarily for releasing unmanaged resources. When working with instance members that are IDisposable implementations, it’s common to cascade Dispose calls. There are additional reasons for implementing Dispose, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.

The .NET garbage collector does not allocate or release unmanaged memory. The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. The dispose pattern is used for objects that implement the IDisposable interface, and is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. This is because the garbage collector is unable to reclaim unmanaged objects.

To help ensure that resources are always cleaned up appropriately, a Dispose method should be idempotent, such that it is callable multiple times without throwing an exception. Furthermore, subsequent invocations of Dispose should do nothing.

The code example provided for the GC.KeepAlive method shows how garbage collection can cause a finalizer to run, while an unmanaged reference to the object or its members is still in use. It may make sense to utilize GC.KeepAlive to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.

Tip

With regard to dependency injection, when registering services in an IServiceCollection, the service lifetime is managed implicitly on your behalf. The IServiceProvider and corresponding IHost orchestrate resource cleanup. Specifically, implementations of IDisposable and IAsyncDisposable are properly disposed at the end of their specified lifetime.

For more information, see Dependency injection in .NET.

Safe handles

Writing code for an object’s finalizer is a complex task that can cause problems if not done correctly. Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. On Windows it might identify a handle while on Unix, a file descriptor. It provides all of the logic necessary to ensure that this resource is released once and only once, when the SafeHandle is disposed of or when all references to the SafeHandle have been dropped and the SafeHandle instance is finalized.

The System.Runtime.InteropServices.SafeHandle is an abstract base class. Derived classes provide specific instances for different kinds of handle. These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. For example, SafeFileHandle derives from SafeHandle to wrap IntPtrs that identify open file handles/descriptors, and overrides its SafeHandle.ReleaseHandle() method to close it (via the close function on Unix or CloseHandle function on Windows). Most APIs in .NET libraries that create an unmanaged resource will wrap it in a SafeHandle and return that SafeHandle to you as needed, rather than handing back the raw pointer. In situations where you interact with an unmanaged component and get an IntPtr for an unmanaged resource, you can create your own SafeHandle type to wrap it. As a result, few non-SafeHandle types need to implement finalizers; most disposable pattern implementations only end up wrapping other managed resources, some of which may be SafeHandles.

The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

  • The SafeFileHandle, SafeMemoryMappedFileHandle, and SafePipeHandle class, for files, memory mapped files, and pipes.
  • The SafeMemoryMappedViewHandle class, for memory views.
  • The SafeNCryptKeyHandle, SafeNCryptProviderHandle, and SafeNCryptSecretHandle classes, for cryptography constructs.
  • The SafeRegistryHandle class, for registry keys.
  • The SafeWaitHandle class, for wait handles.

Dispose() and Dispose(bool)

The IDisposable interface requires the implementation of a single parameterless method, Dispose. Also, any non-sealed class should have an additional Dispose(bool) overload method.

Method signatures are:

  • public non-virtual (NotOverridable in Visual Basic) (IDisposable.Dispose implementation).
  • protected virtual (Overridable in Visual Basic) Dispose(bool).

The Dispose() method

Because the public, non-virtual (NotOverridable in Visual Basic), parameterless Dispose method is called when it is no longer needed (by a consumer of the type), its purpose is to free unmanaged resources, perform general cleanup, and to indicate that the finalizer, if one is present, doesn’t have to run. Freeing the actual memory associated with a managed object is always the domain of the garbage collector. Because of this, it has a standard implementation:

public void Dispose()
{
    // Dispose of unmanaged resources.
    Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}
Public Sub Dispose() _
    Implements IDisposable.Dispose
    ' Dispose of unmanaged resources.
    Dispose(True)
    ' Suppress finalization.
    GC.SuppressFinalize(Me)
End Sub

The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects’ Object.Finalize override. Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Note that the actual cleanup is performed by the Dispose(bool) method overload.

The Dispose(bool) method overload

In the overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

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

    if (disposing)
    {
        // TODO: dispose managed state (managed objects).
    }

    // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
    // TODO: set large fields to null.

    _disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
     If disposed Then Exit Sub	

     ' A block that frees unmanaged resources.
     
     If disposing Then
         ' Deterministic call…
         ' A conditional block that frees managed resources.    	
     End If
     
     disposed = True
End Sub

Important

The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. In other words, it is true when deterministically called and false when non-deterministically called.

The body of the method consists of three blocks of code:

  • A block for conditional return if object is already disposed.

  • A block that frees unmanaged resources. This block executes regardless of the value of the disposing parameter.

  • A conditional block that frees managed resources. This block executes if the value of disposing is true. The managed resources that it frees can include:

    • Managed objects that implement IDisposable. The conditional block can be used to call their Dispose implementation (cascade dispose). If you have used a derived class of System.Runtime.InteropServices.SafeHandle to wrap your unmanaged resource, you should call the SafeHandle.Dispose() implementation here.

    • Managed objects that consume large amounts of memory or consume scarce resources. Assign large managed object references to null to make them more likely to be unreachable. This releases them faster than if they were reclaimed non-deterministically.

If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. The implementer is responsible for ensuring that the false path doesn’t interact with managed objects that may have been disposed. This is important because the order in which the garbage collector disposes managed objects during finalization is non-deterministic.

Cascade dispose calls

If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform cleanup through the Dispose method. In this example, the class is sealed (or NotInheritable in Visual Basic).

using System;

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Tip

There are cases when you may want to perform null-checking in a finalizer (which includes the Dispose(false) method invoked by a finalizer), one of the primary reasons is if you’re unsure whether the instance got fully initialized (for example, an exception may be thrown in a constructor).

Implement the dispose pattern

All non-sealed classes (or Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. If you implement the dispose pattern for any potential base class, you must provide the following:

  • A Dispose implementation that calls the Dispose(bool) method.
  • A Dispose(bool) method that performs the actual cleanup.
  • Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer, so you do not have to write one yourself.

Important

It is possible for a base class to only reference managed objects, and implement the dispose pattern. In these cases, a finalizer is unnecessary. A finalizer is only required if you directly reference unmanaged resources.

Here’s an example of the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class BaseClassWithSafeHandle : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose() => Dispose(true);

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle.Dispose();
            }

            _disposedValue = true;
        }
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class BaseClassWithSafeHandle : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
        End If

        disposed = True
    End Sub
End Class

Note

The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Note that the example does not properly instantiate its SafeFileHandle object.

Here’s the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

using System;

class BaseClassWithFinalizer : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    ~BaseClassWithFinalizer() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _disposedValue = true;
        }
    }
}
Class BaseClassWithFinalizer : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Dispose managed objects that implement IDisposable.
            ' Assign null to managed objects that consume large amounts of memory or consume scarce resources.
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

Tip

In C#, you implement a finalization by providing a finalizer, not by overriding Object.Finalize. In Visual Basic, you create a finalizer with Protected Overrides Sub Finalize().

Implement the dispose pattern for a derived class

A class derived from a class that implements the IDisposable interface shouldn’t implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. Instead, to clean up a derived class, you provide the following:

  • A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. This method must also call the base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) method passing it the disposing status (bool disposing parameter) as an argument.
  • Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it must call the Dispose(bool) overload with false argument.

Here’s an example of the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle.Dispose();
            }

            _disposedValue = true;
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class DerivedClassWithSafeHandle : Inherits BaseClassWithSafeHandle
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Note

The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Note that the example does not properly instantiate its SafeFileHandle object.

Here’s the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
    // To detect redundant calls
    private bool _disposedValue;

    ~DerivedClassWithFinalizer() => this.Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.
            _disposedValue = true;
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Class DerivedClassWithFinalizer : Inherits BaseClassWithFinalizer
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Dispose managed objects that implement IDisposable.
            ' Assign null to managed objects that consume large amounts of memory or consume scarce resources.
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

See also