In this article we will understand the IDisposable interface of the Microsoft System library. Below are the points that we will look upon:
- Why is IDisposable interface required
- Unmanaged memory
- The .NET Garbage Collector
- Disposable interface in detail
- The Using block along with Dispose
- System Libraries that use IDisposable interface
- Conclusion
Why is the IDisposable Interface required
To understand why do we even require to implement IDisposable interface on any class in C#, let’s take a quick look about what are unmanaged resources, and Garbage Collector.
Unmanaged Memory in C#
While creating a .NET or C# application, you might come up with functionalities in which the application has to interact with resources outside of the .NET environment or the .NET libraries, like handling files, on a distributed system, accessing resources from external libraries, APIs or even allocating memory outside the .NET environment.
The .NET Garbage Collector
The .NET Garbage collector takes care about deallocating the managed memory but the developer has to take care about deallocating the unmanaged memory. The Garbage collector deallocates the objects using the Generations strategy.
Coming back to the IDisposable Interface
The IDisposable interface is implemented on a class to implement the Dispose method in which the developer can Dispose off the unmanaged memory or resources. Now, the caller of the class knows that since IDisposable interface is implemented in the class, hence it can call the Dispose method to deallocate the memory.
So let’s understand it by allocating unmanaged memory in below Example
public class DataManager {
IntPtr pointer;
public void AllocateResource()
{
pointer = Marshal.AllocHGlobal(50000);
}
}
Now we Implement the Disposable interface on this class as follows:
public class DataManager : IDisposable
{
IntPtr pointer;
public void AllocateResource()
{
pointer = Marshal.AllocHGlobal(50000);
}
public void Dispose()
{
Marshal.FreeHGlobal(pointer);
}
}
So now, if the user of our class invokes the Dispose method to deallocate the unmanaged memory.
But we can’t leave it all up to the user to invoke Dispose. Hence, we invoke this method from the finalize method too, since the Garbage Collector invokes the finalize method whenever it decides to deallocate the resources. So now, let’s update the DataManager to implement the finalize:
public class DataManager : IDisposable
{
IntPtr pointer;
~DataManager()
{
Dispose();
}
public void AllocateResource()
{
pointer = Marshal.AllocHGlobal(50000);
}
public void Dispose()
{
// Clear managed resources
// this.repository.Dispose();
// this.repository = null;
//Clear up unmanaged resources
Marshal.FreeHGlobal(pointer);
}
}
Finalize method is overridden using the ~ operator( i.e. implementing the C# destructor)
Since we have overridden the destructor, we are also cleaning up the managed resources along with unmanaged ones. However, The Garbage collector will eventually remove all those managed allocations after invoking the destructor, but it is a good practice to clean it up then and there for better performance impact.
Wait, there’s a catch
Scenario 1
What if the Dispose method is invoked first (which will clean up all memory) and then the finalizer runs from the GC, which will again try to deallocate all the resources. This might result in an ObjectDisposedException. We can take care of this by suppressing the Garbage Collector to invoke the Finalize method for our class using GC.SuppressFinalize. Since we have already cleaned up all resources in Dispose
public void Dispose()
{
//... code to Clear managed resources
//Clear up unmanaged resources
Marshal.FreeHGlobal(pointer);
GC.SuppressFinalize(this);
}
Scenario 2
The scenario in which the finalize method is invoked first and then the disposed method, will also result in same problem. So we introduce a bool in our dispose method, so now When GC invokes the Finalize, only clear the unmanaged resources.
public class DataManager : IDisposable
{
IntPtr pointer;
~DataManager()
{
Dispose(false);
}
public void AllocateResource()
{
pointer = Marshal.AllocHGlobal(50000);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool clearManaged)
{
if (clearManaged)
{
//Clear managed resources . For Example:
//if (this.repository != null)
//{
// this.repository.Dispose();
// this.repository = null;
//}
}
//Clear up unmanaged resources
if (pointer != null)
{
Marshal.FreeHGlobal(pointer);
}
GC.SuppressFinalize(this);
}
}
This way we are sure no matter in which order the dispose and finalize run, the unmanaged resources are always deallocated and there is no deallocation twice. This is known as the disposable pattern.
Let’s see it in action
We Implement the static main method as below to use our DataManager class
static void Main(string[] args)
{
DataManager dm = new DataManager();
dm.AllocateResource();
Console.WriteLine("allocated");
Console.ReadKey();
dm.Dispose();
Console.WriteLine("released");
Console.ReadKey();
}
On checking the performance profiler :

Why implement interface instead of a simple method
You might wonder what is the significance of implementing the Dispose method using an interface. Why not implement it directly? To check out the significance of interfaces, we have written a detailed blog post here.
The Using block along with Dispose
The Using block ensures to invoke the Disposed method at the end of it. That is the reason that only the types that implement IDisposable can be used with using block. Integrating using block with our code as below:
static void Main(string[] args)
{
using (DataManager dm = new DataManager())
{
Console.WriteLine("allocated");
dm.AllocateResource();
} //Automatic call of Dispose at this point
Console.WriteLine("released");
}
The using block converts into the try-finally block internally, so even if any exception occurs, it always invokes Dispose.
System Libraries that implement IDisposable
There are many libraries within the .NET framework that access system resources and contain the IDisposable interface’s Dispose either directly, or by base class. Few examples:
- System.IO.FileStream
- System.Data.SqlClient.SqlConnection
- System.Net.Http.HttpClient
Conclusion
We discussed about the Disposable pattern in detail with sample code and performance analysis. Please let us know about your suggestions in the comment box.