The Disposal Pattern in .NET: Managing Unmanaged Resources and Implementing Clean-Up
Tags: dotnettips, resourcemanagement, disposalpattern, codequality
Published: 2023-09-20 09:50:43

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

```csharp
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);
    }
}
```

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.

4. 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.

5. 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:

```csharp
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
}
```

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.