Dispose Pattern

Resource management is one of the most important concepts in .NET development. While the .NET Garbage Collector (GC) automatically manages memory, it does not immediately release unmanaged resources such as file handles, database connections, network sockets, or native OS resources.

What is IDisposable?

IDisposable is a built-in .NET interface that defines a contract for deterministic resource cleanup. The interface itself does not release resources; instead, the implementing class provides the Dispose() method that contains the logic to free resources it owns.

How Does Dispose() Actually Work?

The IDisposable interface itself does not contain any implementation. It simply defines a contract by declaring the Dispose() method.

public interface IDisposable{    void Dispose();}

The implementing class is responsible for providing the actual cleanup logic. When Dispose() is called—either explicitly by your code or implicitly by the using statement—the runtime invokes the implementation provided by that class.

For example, consider FileStream. It implements IDisposable because it owns an operating system file handle.

using var stream = new FileStream("internal_work.csv", FileMode.Open);

When execution leaves the using block, the C# compiler automatically calls:

stream.Dispose();

Since stream is a FileStream object, the FileStream.Dispose() implementation is executed. Internally, it performs tasks such as:

  • Flushing any buffered data to the file.
  • Closing the file handle.
  • Releasing operating system resources.
  • Marking the object as disposed to prevent further use.

Similarly, classes like SqlConnection, HttpClient, MemoryStream, StreamWriter, and Socket each provide their own implementation of Dispose() based on the resources they own.

In other words, the caller simply invokes Dispose(), while the implementing class determines exactly what cleanup operations need to be performed.

Managed vs Unmanaged Resources

Understanding the difference is essential.

Managed ResourcesUnmanaged Resources
StringFile Handle
List<T>Database Connection Handle
DictionaryNetwork Socket
ArraysWindow Handle
CLR ObjectsNative Memory

The Garbage Collector only cleans managed memory.

Developers are responsible for releasing unmanaged resources.

What Happens If Dispose Isn’t Called?

Imagine opening a file:

var stream = new FileStream("internal_work.csv", FileMode.Open);

If you never dispose it:

  • The file may stay locked.
  • Native handles remain allocated.
  • Memory pressure increases.
  • Resource cleanup depends on when the Garbage Collector runs.

Since GC execution is non-deterministic, relying on it for releasing important resources is not recommended.


The using Statement

The using statement ensures that Dispose() is always executed, even if an exception occurs.

using(var stream = new FileStream("internal_work.csv", FileMode.Open))
{
    // Read or write
}

The compiler internally converts it into:

FileStream stream = new FileStream("internal_work.csv", FileMode.Open);

try
{
    // Read or write
}
finally
{
    stream?.Dispose();
}

Modern C# also supports:

using var stream = new FileStream("internal_work.csv", FileMode.Open);

This is cleaner and recommended for most scenarios.

When Should You Implement IDisposable?

Implement IDisposable when your class:

  • Owns unmanaged resources.
  • Owns disposable managed objects and controls their lifetime.
  • Allocates native memory.
  • Opens database connections.
  • Uses file streams.
  • Uses sockets or network connections.

Do not implement IDisposable for simple POCO classes that only contain managed data.

Best Practices

  • Prefer the using statement or using var.
  • Implement IDisposable only when necessary.
  • Dispose managed resources before unmanaged resources.
  • Avoid finalizers unless you own unmanaged resources.
  • Call GC.SuppressFinalize(this) inside Dispose().
  • Make Dispose() safe to call multiple times.
  • Never access disposed objects.

IDisposable vs Finalizer

IDisposableFinalizer
Called explicitlyCalled by Garbage Collector
Deterministic cleanupNon-deterministic cleanup
Releases managed and unmanaged resourcesReleases unmanaged resources only
Better performanceSlower
Preferred approachSafety net only
Is Dispose() called automatically?

No. It is called automatically only when you use the using statement or using var.

Does the Garbage Collector call Dispose()?

No. The Garbage Collector calls the finalizer, not Dispose().

Does every class need IDisposable?

No. Only classes responsible for resource cleanup should implement it.

Is FileStream IDisposable?

Yes. FileStream implements IDisposable because it manages operating system file handles.

Is HttpClient IDisposable?

Yes. Although HttpClient implements IDisposable, it is generally recommended to reuse instances or create them using IHttpClientFactory in ASP.NET Core instead of creating and disposing one per request.

Share Button

Leave a Reply

Your email address will not be published. Required fields are marked *