DEV Community

Cover image for The Disposal Pattern in .NET: Managing Unmanaged Resources and Implementing Clean-Up
waelhabbal
waelhabbal

Posted on

The Disposal Pattern in .NET: Managing Unmanaged Resources and Implementing Clean-Up

Introduction:
In the .NET, resources can be broadly categorized as managed and unmanaged. Managed resources are automatically managed by the runtime through garbage collection, while unmanaged resources are external resources that are not directly controlled by the runtime. Unmanaged resources include file handles, database connections, network sockets, and other system-level resources. These resources require explicit clean-up to ensure their proper release and prevent resource leaks.

Why Unmanaged Resources are not Managed by the Runtime:
Unmanaged resources are not managed by the runtime because they are typically acquired and released using platform-specific APIs or external libraries that are beyond the control of the .NET runtime. Examples include native Windows APIs or third-party libraries. Since the runtime doesn't have knowledge of how these resources are acquired and released, it cannot automatically manage them through garbage collection.

Let's see this Code which is a Disposable pattern in Visual Studio

internal class Sample : IDisposable
{
    private bool disposedValue;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: Dispose managed state (managed objects)
                // Perform clean-up for managed resources here
            }

            // TODO: Free unmanaged resources (unmanaged objects) and override finalizer
            // Perform clean-up for unmanaged resources here
            // Set large fields to null to aid garbage collection

            disposedValue = true;
        }
    }

    // // TODO: Override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
    // ~Sample()
    // {
    //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
    //     Dispose(disposing: false);
    // }

    public void Dispose()
    {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. The class Sample implements the IDisposable interface, indicating that it encapsulates resources that need explicit clean-up.

  2. The Dispose(bool disposing) method is the core of the disposal pattern. It is responsible for releasing both managed and unmanaged resources. The disposing parameter indicates whether the method is called from the public Dispose() method or from the finalizer.

  3. Inside the Dispose(bool disposing) method:
    a. The if (!disposedValue) check ensures that the method is not called multiple times.

b. If disposing is true, it means the method is called from the public Dispose() method, and we can safely dispose of managed resources. This is the place to release any managed objects held by the class.

c. After disposing of managed resources, the method proceeds to release unmanaged resources. This is the place to free any unmanaged objects, such as closing file handles or releasing external resources acquired through platform-specific APIs.

d. Finally, the disposedValue flag is set to true to indicate that disposal has been performed.

  1. The public Dispose() method acts as a convenient way to invoke the disposal process. It calls the Dispose(bool disposing) method, passing true to indicate that it is called from the public method. It also suppresses the finalization of the object by calling GC.SuppressFinalize(this), as the disposal process has already been performed.

  2. The commented-out finalizer (~Sample()) is included in the code template but should only be enabled if the class holds unmanaged resources that require finalization. If the class only encapsulates managed resources, the finalizer is not necessary.

Additionally, the disposal pattern allows for a more concise and professional way of calling the Dispose() method using the C# language feature called "using statement." The using statement ensures that the Dispose() method is called automatically at the end of the block, even if an exception occurs. This approach enhances code readability and maintainability.

Here's an example of how the using statement can be used with the Sample class:

using (var sample = new Sample())
{
    // Code block where the 'sample' object is used

    // No need to explicitly call Dispose() here
    // The 'using' statement will take care of it
}
Enter fullscreen mode Exit fullscreen mode

By embracing the using statement, developers can clearly indicate the scope of resource usage and ensure the timely and proper disposal of resources.

Conclusion:
Unmanaged resources in .NET need explicit clean-up as they are not managed by the runtime's garbage collector. By implementing the disposal pattern and following best practices, such as the use of the IDisposable interface, we ensure that unmanaged resources are properly released. The provided code sample demonstrates the structure of the disposal pattern and illustrates where to perform clean-up for both managed and unmanaged resources.

Top comments (0)