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 Resources | Unmanaged Resources |
|---|---|
| String | File Handle |
| List<T> | Database Connection Handle |
| Dictionary | Network Socket |
| Arrays | Window Handle |
| CLR Objects | Native 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
usingstatement orusing var. - Implement
IDisposableonly when necessary. - Dispose managed resources before unmanaged resources.
- Avoid finalizers unless you own unmanaged resources.
- Call
GC.SuppressFinalize(this)insideDispose(). - Make
Dispose()safe to call multiple times. - Never access disposed objects.
IDisposable vs Finalizer
| IDisposable | Finalizer |
|---|---|
| Called explicitly | Called by Garbage Collector |
| Deterministic cleanup | Non-deterministic cleanup |
| Releases managed and unmanaged resources | Releases unmanaged resources only |
| Better performance | Slower |
| Preferred approach | Safety 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.
