In .Net we have two kind of resources.
- Managed resource
- Unmanaged resource
To understand IDisposable pattern we need to understand those concepts.
Managed resource
Managed resource are those which are created and handled by CLR (common language runtime). Garbage collector do a very pretty job to clean up the managed resources. We don’t need to manually clean those resources.
Unmanaged resource
Unmanaged resource are those which are outside of the scope of CLR (common language runtime). Garbage collector most of the time cannot clean up those resources. We developer need to manually clean those resources as soon as they are done.
To handle unmanaged resources .Net give us IDisposable interface. We have to use that pattern to clean
Unmanaged resources.
So to implement IDisposable pattern we have to inherit that interface and implement the pattern. Basic code looks like this. I will explain each line step by step.
public class Excel : IDisposable
{
public string FileObject { get; set; }
public string ConnectionObject { get; set; }
public void OpenExcelFile()
{
FileObject = "demo";
//open excel from specific file path.
//creating unmanaged resources
}
public void OpenManageonnection()
{
ConnectionObject = "Managed Connection opened";
}
}
This is my class which implement IDisposable interface. Here I created demo managed and unmanaged class just to show the purpose of this tutorial. As you can see I have created both resources, now it’s the time to implement pattern. After implementing the pattern we can see the two methods.
Dispose
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
Dispose is the main method which gets called when someone implement dispose method by finally scope or using statements. What dispose does is to call dispose method which is virtual and it can be inherited by child classes. This is the dispose virtual method.
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
Console.WriteLine("managed object clean");
ConnectionObject = "";
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
if (FileObject != "")
{
Console.WriteLine("unmanaged object clean");
FileObject = "";
}
disposedValue = true;
}
}
This method gets called by Dispose with true value and also get called by Finalizer with false value. disposedValue is a local flag value which get track that this method can only execute once. So if the disposing is true then both managed and unmanaged objects will cleaned up. disposedValue then set to true so that it won’t execute again.
After disposing we have to set that object as it won’t get into the finalizing queue and doesn’t get cleaned again. So we need to call GC for that.
GC.SuppressFinalize(this);
This will do the job for us.
Initialization of Dispose pattern
In the calling method we can simply call this class like this
static void Main(string[] args)
{
using (Excel excel = new Excel())
{
excel.OpenExcelFile();
excel.OpenManageonnection();
}
}
Using statement will automatically call the dispose method of Excel class and thus cleaned up the memory of this class.
Finalizer
Finalizer in C# get called by Garbage collector to remove unmanaged resources. But we already clean our resource. But what will happen when developer forget to using the using statement? Then those resources won’t get cleaned. This is what finalizer is useful.
GC automatically call finalizer to clean unmanaged resources. So even if we forgot to clean the unmanaged resources GC will take care if we use finalizer.
~Excel()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Console.WriteLine("Finilizing");
Dispose(disposing: false);
}
As we can see in finalizer method we call dispose method with false value, which will only clean the unmanaged resources. We don’t need to worry about managed resources, GC will simply take care of that.
So now the IDisposable pattern is complete. We can put some custom code in dispose method if we wish.
The full implementation is looks like that.
public class Excel : IDisposable
{
private bool disposedValue;
public string FileObject { get; set; }
public string ConnectionObject { get; set; }
public void OpenExcelFile()
{
FileObject = "demo";
//open excel from specific file path.
//creating unmanaged resources
}
public void OpenManageonnection()
{
ConnectionObject = "Managed Connection opened";
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
Console.WriteLine("managed object clean");
ConnectionObject = "";
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
if (FileObject != "")
{
Console.WriteLine("unmanaged object clean");
FileObject = "";
}
disposedValue = true;
}
}
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
~Excel()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Console.WriteLine("Finilizing");
Dispose(disposing: false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
Console.WriteLine("disposing start");
GC.SuppressFinalize(this);
}
}
It is recommended if you don’t have any unmanaged resource then don’t use Finalizer as it is a costly process which may hamper performance.
If you have any feedback about this topic feel free to comment.
Top comments (0)