Click here to Skip to main content
Click here to Skip to main content

Understanding and Implementing IDisposable Interface - A Beginner's Tutorial

By , 1 Jul 2012
 

Introduction

In this article, we will discuss the often confused topic of Implementing the IDisposable interface. We will try to see when should be need to implement the IDisposable interface and what is the right way of implementing it.

Background 

Often in our application development we need to aquire some resources. These resources coould be files that we want to use from a clustered storage drive, database connections etc. The important thing to remember while using these resources is to release these resources once we are done with it.

C++ programmers are very well aware of the the concept of Resource Acquisition is Initialization(RAII). This programing idiom states that of an object want to use some resources then it is his responsibility to acquire the resource and release it. It should do so in normal conditions and in exceptional/error conditions too. This idiom is implemented very beautifully using constructors, destructors and exception handling mechanism.

Let us not digress and see how we can have a similar idiom in C# using IDisposable pattern.

Using the code 

Before looking into the details of IDisposable interface let us see how we typically use the resources, or what are the best practices to use the resources. We should always acquire the resources assuming two things. One we could get the resource and use it successfully. secondly there could be some error/exception while acquiring the resource. So the resource acquisition and usage should always happen inside a try block. The resource release should always be in the finally block. As this finally block will be executed always. Let us see a small code snippet to understand a little better.

Using Try-finally to manage resources

//1. Let us first see how we should use a resource typically
TextReader tr = null;

try
{
    //lets aquire the resources here                
    tr = new StreamReader(@"Files\test.txt");

    //do some operations using resources
    string s = tr.ReadToEnd();

    Console.WriteLine(s);
}
catch (Exception ex)
{
    //Handle the exception here
}
finally
{
    //lets release the aquired resources here
    if (tr != null)
    {
        tr.Dispose();
    }
}

Putting 'using' in place to manage resources

The other way to do the same thing us to use using, this way we only have to think about the resource acquisition and the exception handling. the resource release will be done automatically because the resource acquisition is wrapped inside the using block. Lets do the same thing we did above putting the using block in place. 

//2. Let us now look at the other way we should do the same thing (Recommended version)
TextReader tr2 = null;

//lets aquire the resources here                
try
{
    using (tr2 = new StreamReader(@"Files\test.txt"))
    {

        //do some operations using resources
        string s = tr2.ReadToEnd();

        Console.WriteLine(s);
    }
}
catch (Exception ex)
{
    //Handle the exception here
}

Both the above approaches are essentially the same but the only difference is that in the first approach we have to release the resource explicitly where as in the second approach the resource release was done automatically. using block is the recommended way of doing such things as it will do the clean up even if the programmers forget to do it. 

The use of 'using' block is possible because the TextReader class in the above example is implementing IDisposable pattern.  

A note on Finalizers

Finalizers are like destructors. they will get called whenever the object goes out of scope. We typically don't need to implement Finalizers but if we are planning to implement IDisposable pattern and at the same time we want the resource cleanup to happen when the local object goes out of scope then we will have to have a Finalizer implemented in our class. 

class SampleClass
{
    ~SampleClass()
    {
        //This is a Finalizer
    }
}

When and why we need to Implement IDisposable

Now we know how the resource acquisition and release should be done ideally. we also know that the recommended way of doing this is using statement. Now its time to see why we might need to know more about implementing the IDisposable pattern ourselves. 

Lets assume that we are writing a class which will be reused all across the project. This class will acquire some resources. To acquire these resources our class will be needing some managed objects(like in above example) and some unmanaged stuff too(like using a COM component or having some unsafe code with pointers). 

Now since our class is acquiring resources, the responsibility of releasing these resources also lies with the class. Let us have the class with all the resource aquisition and release logic in place.

class MyResources
{
    //The managed resource handle
    TextReader tr = null;  
   

    public MyResources(string path)
    {
        //Lets emulate the managed resource aquisition
        Console.WriteLine("Aquiring Managed Resources");
        tr = new StreamReader(path);
       
        //Lets emulate the unmabaged resource aquisition
        Console.WriteLine("Aquiring Unmanaged Resources");        
    }

    void ReleaseManagedResources()
    {
        Console.WriteLine("Releasing Managed Resources");
        if (tr != null)
        {
            tr.Dispose();
        }
    }

    void ReleaseUnmangedResources()
    {
        Console.WriteLine("Releasing Unmanaged Resources");        
    }
}

We have the class ready with the resource allocation and deallocation code. We also have the functions to use the class. Now we want to use this class following the guidelines earlier in this article. i.e.

  • Using the object in a try-finally block, acquire resources in try block and release them in finally.
  • Using the object in a using block, the resource release will be done automatically.
  • The object when goes out of scope it should get disposed automatically as it was a local variable.

Implementing IDisposable

Now if we need to perform the clean up using while using our object and facilitate all the above mentioned functionalities we need to implement the IDisposable pattern. Implementing IDisposable pattern will force us to have a Dispose function.

Secondly if the user want to use the try-finally approach then also he can call this Dispose function and the object should release all the resources. 

Lastly and most importantly, lets have a Finalizer that will release the unmanaged resources when the object goes out of scope. The important thing here is to do this finalize only if the programmer is not using the 'using' block and not calling the Dispose explicitly in a finally block.

For now lets create the stubs for these functions.

class MyResources : IDisposable
{
    // ...
    #region IDisposable Members

    public void Dispose()
    {
        
    }

    #endregion
    
    ~MyResources()
    {

    }
}

So to understand the possible scenario how this class might need disposal let us look at the following possible use cases for our class. we will also see what should be done in each possible scenario.

  1. The user will not do anything to relase the resource. We have to take care of releasing resource in finalizer.
  2. The user will use try-finally block. We need to do the clean up and ensure that finalizer will not do it again.
  3. The user will put a using 'block'. We need to do the clean up and ensure that finalizer will not do it again.

So here is the standard way of doing this Dispose business. It is also known as dispose pattern. Lets see how this should be done to achieve all we wanted.  

public void Dispose()
{
    // If this function is being called the user wants to release the
    // resources. lets call the Dispose which will do this for us.
    Dispose(true);

    // Now since we have done the cleanup already there is nothing left
    // for the Finalizer to do. So lets tell the GC not to call it later.
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{            
    if (disposing == true)
    {
        //someone want the deterministic release of all resources
        //Let us release all the managed resources
        ReleaseManagedResources();
    }
    else
    {
        // Do nothing, no one asked a dispose, the object went out of
        // scope and finalized is called so lets next round of GC 
        // release these resources
    }

    // Release the unmanaged resource in any case as they will not be 
    // released by GC
    ReleaseUnmangedResources();
}        

~MyResources()
{
    // The object went out of scope and finalized is called
    // Lets call dispose in to release unmanaged resources 
    // the managed resources will anyways be released when GC 
    // runs the next time.
    Dispose(false);
}

So now the complete definition of our class looks like this. This time I have added some print messages to the methods so that we can see how things work when we use different approaches.

class MyResources : IDisposable
{
    //The managed resource handle
    TextReader tr = null;    

    public MyResources(string path)
    {
        //Lets emulate the managed resource aquisition
        Console.WriteLine("Aquiring Managed Resources");
        tr = new StreamReader(path);
        
        //Lets emulate the unmabaged resource aquisition
        Console.WriteLine("Aquiring Unmanaged Resources");     
        
    }

    void ReleaseManagedResources()
    {
        Console.WriteLine("Releasing Managed Resources");
        if (tr != null)
        {
            tr.Dispose();
        }
    }

    void ReleaseUnmangedResources()
    {
        Console.WriteLine("Releasing Unmanaged Resources");
    }

    public void ShowData()
    {
        //Emulate class usage
        if (tr != null)
        {
            Console.WriteLine(tr.ReadToEnd() + " /some unmanaged data ");
        }
    }
            
    public void Dispose()
    {
        Console.WriteLine("Dispose called from outside");
        // If this function is being called the user wants to release the
        // resources. lets call the Dispose which will do this for us.
        Dispose(true);

        // Now since we have done the cleanup already there is nothing left
        // for the Finalizer to do. So lets tell the GC not to call it later.
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine("Actual Dispose called with a " + disposing.ToString());
        if (disposing == true)
        {
            //someone want the deterministic release of all resources
            //Let us release all the managed resources
            ReleaseManagedResources();
        }
        else
        {
            // Do nothing, no one asked a dispose, the object went out of
            // scope and finalized is called so lets next round of GC 
            // release these resources
        }

        // Release the unmanaged resource in any case as they will not be 
        // released by GC
        ReleaseUnmangedResources();

    }        

    ~MyResources()
    {
        Console.WriteLine("Finalizer called");
        // The object went out of scope and finalized is called
        // Lets call dispose in to release unmanaged resources 
        // the managed resources will anyways be released when GC 
        // runs the next time.
        Dispose(false);
    }
}

Let us use this class first with a try-finally block

//3. Lets call out class using try-finally block
MyResources r = null;

try
{
    r = new MyResources(@"Files\test.txt");
    r.ShowData();
}
finally
{
    r.Dispose();
}

The sequence of operations can be understood by these ouptut messages.

Aquiring Managed Resources
Aquiring Unmanaged Resources
This is a test data. / Some unmanaged data.
Dispose called from outside
Actual Dispose called with a True
Releasing Managed Resources
Releasing Unmanaged Resources

Let us use this class putting a using block in place

//4. The using block in place
MyResources r2 = null;
using (r2 = new MyResources(@"Files\test.txt"))
{
    r2.ShowData();
}

The sequence of operations can be understood by these ouptut messages.

Aquiring Managed Resources
Aquiring Unmanaged Resources
This is a test data. / Some unmanaged data.
Dispose called from outside
Actual Dispose called with a True
Releasing Managed Resources
Releasing Unmanaged Resources

Let us use this and leave the resource release to GC. the unmanaged resources should still be cleaned up when the finalizer gets called.

//5. Lets not do anything and the GC and take care of managed data
//   we will let our finalizer to clean the unmanaged data
MyResources r3 = new MyResources(@"Files\test.txt");
r3.ShowData();

The sequence of operations can be understood by these ouptut messages.

Aquiring Managed Resources
Aquiring Unmanaged Resources
This is a test data. / Some unmanaged data.
Finalizer called
Actual Dispose called with a False
Releasing Unmanaged Resources

Points of Interest

Implementing IDisposable has always been a little confusing for the beginners. I have tried to jot down my understanding of when to implement this interface and what is the right way if implementing it. Although this article is written from a beginner's perspective, I hope this article has been informative and helpful to someone.  

History

  • 02 July 2012: First Version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rahul Rajat Singh
Software Developer (Senior)
India India
Member
I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems.
 
Some CodeProject Achievements:
  • 9th in Best Web Dev article of March 2013
  • 7th in Best Web Dev article of January 2013
  • 2nd in Best C# article of December 2012
  • 5th in Best overall article of December 2012
  • 5th in Best C# article of October 2012
  • 4th in Best Web Dev article of September 2012
  • 3rd in Best C# article of August 2012
  • 5th in Best Web Dev article of August 2012
  • 5th in Best Web Dev article of July 2012
  • 3rd in Best Overall article of June 2012
  • 2nd in Best Web Dev article of June 2012
  • 5th in Best Web Dev article of May 2012
  • 6th in Best Web Dev article of April 2012
  • 4th in Best C++ article of April 2012
  • 5th in Best C++ article of March 2012
  • 7th in Best Web Dev article of March 2012
  • 5th in Best Web Dev article of February 2012
  • 7th in Best Web Dev article of February 2012
  • 9th in Best Web Dev article of February 2012
  • 5th in Best C++ article of February 2012

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membertanujtomer14 Mar '13 - 3:48 
good understanding
GeneralDon't Dispose twice [modified]memberjfos20 Nov '12 - 1:24 
Nice article, but the Microsoft docs for Dispose[^] also specifiy that an exception should not be raised during dispose, which could happen if Dispose is called twice.
 
This can be done in one of two ways (or both).
1. Setup a instance variable and set it to true the first time Disposed is called. Of course, check it when entering Disposed.
2. Set the "inner" object you just disposed to null so you can't dispose it again.
 
private bool _hasDisposed = false;
 
/*
 * other code...
 */
 
void ReleaseManagedResources()
{
    Console.WriteLine("Releasing Managed Resources");
    if (tr != null)
    {
        tr.Dispose();
        tr = null;
    }
}
 

/*
 * other code...
 */
 
protected virtual void Dispose(bool disposing)
{
    if (_hasDisposed == false)
    {
        Console.WriteLine("Actual Dispose called with a " + disposing.ToString());
        if (disposing == true)
        {
            //someone want the deterministic release of all resources
            //Let us release all the managed resources
            ReleaseManagedResources();
        }
        else
        {
            // Do nothing, no one asked a dispose, the object went out of
            // scope and finalized is called so lets next round of GC 
            // release these resources
        }
 
        // Release the unmanaged resource in any case as they will not be 
        // released by GC
        ReleaseUnmangedResources();
        
        _hasDisposed = true;
    }
    else
    {
        Console.WriteLine("Already called Dispose.");
    }
}


modified 20 Nov '12 - 15:13.

AnswerRe: Don't Dispose twicememberRahul Rajat Singh20 Nov '12 - 1:27 
This is actually a very valuable suggestion. +5.
Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore, Dream. Discover.

Questionuse of IDisposable interfacememberfarouk tifratene4 Jul '12 - 1:46 
Hi Rahul,
Thank you so much for this article. I had a job interview recently and i was actually asked about this interface and the use of using clause. After reading your article and run your code i now have a very good understanding on this implementation. once again thank you so much.
Farouk, England
AnswerRe: use of IDisposable interfacememberRahul Rajat Singh4 Jul '12 - 2:49 
I am glad it helped. thanks for the response.
Every now and then say, "What the Elephant." "What the Elephant" gives you freedom. Freedom brings opportunity. Opportunity makes your future.

GeneralMy vote of 4memberMember 87232452 Jul '12 - 8:51 
informative and apt.
GeneralMy vote of 3memberjockey4her2 Jul '12 - 8:12 
I'm a beginner, and I didn't even know what this was, or how I would use it, or need to. Doesn't C# do this by itself? This seems like a C thing.
GeneralFinalizers are not destructorsmemberJohn Brett2 Jul '12 - 2:30 
A Finalizer may be called after the object has gone out of scope, but at the convenience of the GC and not deterministically.
It is quite different from a C/C++ style destructor in that respect.
Furthermore, simply declaring one is an overhead for the GC, and should be done only when necessary.
Typically, this is used for limited unmanaged resources.
 
John
(see http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx[^] again)
GeneralRe: Finalizers are not destructorsmemberMember 84469732 Jul '12 - 2:42 
this ^
 
it is cheaper to seal up the class and skip the finalizer and the virtual Dispose method altogether. hope your post won't get downvoted like mine
 
another thing is that I can tell from own experience of fixing poorly implemented IDisposibles (namely abstract DisposibleBase, ViewModelBase etc with finalizers and virtual Dispose methods), there is no "beginner stage" in implementing IDisposible. You either do it right or wrong. You can't copy half of an article and slap "A beginners tutorial" on it.
GeneralRe: Finalizers are not destructorsmvpRichard MacCutchan2 Jul '12 - 6:56 
Member 8446973 wrote:
hope your post won't get downvoted like mine

I did not vote your comment, but I suspect it got downvoted for the first sentence (txtspk/streettalk), you posted two URL's without making them into clickable links, and you really could have provided a more constructive comment to justify your vote.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 2 Jul 2012
Article Copyright 2012 by Rahul Rajat Singh
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid