Dispose Pattern

The Dispose method is used to release unmanaged resources that an object holds. Unmanaged resources include things like file handles, network connections, database connections, and memory from other systems (such as native OS calls). Properly releasing these resources is crucial for avoiding resource leaks and ensuring that your application runs efficiently.

Unmanaged Resources

  • File Handles: Resources used to read from or write to files.
  • Network Connections: Connections to network services or remote servers.
  • Database Connections: Connections to databases that need to be explicitly closed.
  • Memory from Other Systems: Memory allocated through native code or APIs that needs manual management.

Many .NET objects (e.g., Stream, FileStream, SqlConnection) rely on unmanaged resources and implement the IDisposable interface to ensure they are properly cleaned up.

IDisposable Interface

The IDisposable interface is designed for types that need to release unmanaged resources. It contains a single method, Dispose, which should be implemented to free these resources.

Most of the time, you'll be implementing Dispose to clean up unmanaged resources used by that class (if they'r not already being cleaned up elsewhere).

Example of IDisposable Interface

The resource below uses a SqlConnection which uses unmanaged resources. It's EXTREMELY important to call Dispose on the connection when you're done with it. For this example, since the MyResource class creates and manages the SqlConnection, it should also be responsible for disposing of it.

public class MyResource : IDisposable
{
    private bool _disposed = false; // To detect redundant calls
    private SqlConnection _connection;

    // Implement IDisposable
    public void Dispose()
    {
        if (!disposed)
        {
            _connection.Dispose();

            // Free your own state (unmanaged objects).
            disposed = true;
        }
    }
}

using Statement

The using statement provides a convenient syntax that ensures the Dispose method is called automatically at the end of the block, even if an exception occurs.

For our example above, you would then use the MyResource class like this:

using (var myResource = new MyResource())
{
    myResource.UseResource();
}

You can also use the using statement at the beginning of a variable declaration to ensure it's disposed of when the scope ends:

using var myResource = new MyResource();
myResource.UseResource();

//automatically disposed of when myResource goes out of scope

🌶️🌶️🌶️ The inline using statement looks great, but I vastly prefer to use my using statements within code blocks so that it's explicitly clear when the resource is expected to be disposed of.

Async Dispose

With asynchronous programming, you can use await using to asynchronously dispose of objects that implement IAsyncDisposable. This is useful for I/O-bound resources that benefit from asynchronous disposal.

Example

await using (var stream = new FileStream("file.txt", FileMode.Open))
{
    // Use the stream
}

🌶️🌶️🌶️ Rule of thumb: if your method is async and your resource is disposable, you should use await using - otherwise, using using is fine.