X

All about IDisposable

So, what is IDisposable and what is disposing of objects? This is something I teach to developers classes before they are going to their first school practice at some company. One of important concepts is disposing. Here’s my story about it.

Unmanaged resources – why we dispose objects?

Let’s start with concept of unmanaged resources. Almost all of our applications use some resources like file system, database, web services etc. Consuming these resources means also using operating system or low level API-s and services. And this is were need for disposable objects comes.

Suppose you are opening a file. For this you probably create new instance of FileStream class. What happens internally? File is opened using Windows API-s and low level API-s usually use file handle when calling file system functions. For .NET this handle is just a number and only StreamReader class knows what it actually is and how to use it.

If you don’t dispose FileStream class then it keeps the handle and file is held open for your program. Instance of FileStream goes to garbage collection, lives there for a short moment of time and then it’s removed. Operating system has no idea that file it opened can be closed now.

To release unmanaged resources allocated by our classes we use the mechanism called “disposable”.

Example from practice

Once I worked with codebase where previous developers didn’t knew about disposing. Code was written and organized pretty well. Bad problems always came at live servers where suddenly database server was running out of resources.

When I connected to database I saw hundreds of connections in idle state. I made few click on a page and soon the number of idle connections grew a little bit higher. Lucky me – it was system with small number of daily users.

After checking out what’s going on in code I knew what to do – dispose all ADO.NET objects defined in System.Data namespace – connections, commands, data tables etc. It took me few days to apply disposing and reorganizing the code. Those fixes made system stable and database server wasn’t pushed to the limits anymore.

IDisposable interface

All classes that support disposing extend IDisposable interface.

public interface IDisposable
{
    //
    // Summary:
    //     Performs application-defined tasks associated with freeing, releasing, or resetting
    //     unmanaged resources.
    void Dispose();
}

It’s a primitive one defining only one method – Dispose(). But it’s fundamental as almost all components working with unmanaged resources use it.

Example. Using ASP.NET Core dependency injection (DI) to get instances means you don’t need to call Dispose() manually. Life-cycle of instances given by ASP.NET Core DI is controller by DI mechanism.

Disposing objects

Let’s write a simple piece of code that opens and closes file.

class Program
{
    static void Main(string[] args)
    {
        var fileStream = new FileStream("invoices.csv", FileMode.Open);

        // use your file

        fileStream.Dispose();
    }
}

Looking at this code I see some issues:

  • what if unexpected exception is thrown while file is used?
  • possibility to return from method without calling Dispose()
  • it’s easy to forget Dispose() method when we don’t need fileStream anymore

There must be better way to do it.

Using-blocks

To save us from issues mentioned above there’s using-keyword available in C#. To make sure Dispose() method is always called when we don’t need disposable object anymore we can put it using-block.

class Program
{
    static void Main(string[] args)
    {
        using (var fileStream = new FileStream("invoices.csv", FileMode.Open))
        {
            // use your file
        }
    }
}

We don’t have to remember calling Dispose() method anymore as now C# will do it for us the safe way.

But what happens internally? Let’s build the program, delete .pdb file in bin folder and open program .dll file using some .NET disassembler (I’m using free tool called dotPeek by JetBrains).

Let’s take a look at Intermediate Language (IL) representation of our class. If IL is too much for you then it’s enough if you find familiar keywords, classes and methods from the following mess.

.class private auto ansi beforefieldinit
  DisposableDemo.Program
    extends [System.Runtime]System.Object {

  .method private hidebysig static void
    Main(
      string[] args
    ) cil managed
  {
    .entrypoint
    .maxstack 2
    .locals init (
      [0] class [System.Runtime]System.IO.FileStream V_0
    )

    IL_0000: nop

    // [16 7 - 16 60]
    IL_0001: ldstr        "invoices.csv"
    IL_0006: ldc.i4.3
    IL_0007: newobj       instance void [System.Runtime]System.IO.FileStream::.ctor(string, valuetype [System.Runtime]System.IO.FileMode)
    IL_000c: stloc.0      // V_0
    .try
    {
      IL_000d: nop
      IL_000e: nop

      IL_000f: leave.s      IL_001c
    } // end of .try
    finally
    {
      IL_0011: ldloc.0      // V_0
      IL_0012: brfalse.s    IL_001b
      IL_0014: ldloc.0      // V_0
      IL_0015: callvirt     instance void [System.Runtime]System.IDisposable::Dispose()
      IL_001a: nop
      IL_001b: endfinally
    } // end of finally
    IL_001c: ret

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8

    // [22 7 - 22 24]
    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
    IL_0006: nop
    IL_0007: ret

  } }

Notice you don’t see any sign of using-block in disassembly. But still we can draw out a certain pattern and convert it back to our familiar C#.

class Program
{
    static void Main(string[] args)
    {
        var fileStream = new FileStream("invoices.csv", FileMode.Open);

        try
        {
            // use your file
        }
        finally
        {
            fileStream.Dispose();
        }
    }
}

Why is fileStream assigned out from try-block? If opening of file fails then we don’t have file handle anyway and there’s nothing to dispose.

Warning! Be very careful with disposable class design and if possible avoid allocating unmanaged resources in constructor. If you have to do it then be ready to release resources immediately if exception is thrown.

Nesting and stacking of using-statements

It’s not uncommon need to nest using-statements. My record this far has been five using-blocks inside each other. Let’s see the following example.

class Program
{
    static void Main(string[] args)
    {
        using(var fileStream = new FileStream("invoices.csv", FileMode.Open))
        {
            using(var reader = new StreamReader(fileStream))
            {
                // do something with file contents
            }
        }
    }
}

Here I have one using-block inside the other. If we have more blocks to nest then our code turns messy and it’s not same easy to read. Luckily we can use shorter syntax.

class Program
{
    static void Main(string[] args)
    {
        using(var fileStream = new FileStream("invoices.csv", FileMode.Open))
        using(var reader = new StreamReader(fileStream))
        {
            // do something with file contents
        }
    }
}

This way we can pile using-blocks after each other without loosing readability.

Some developers don’t like this style and they argue that it doesn’t give clear idea of code structuring. I don’t agree with it because without using-blocks the same disposable variables will be defined one after another in code.

Using-declarations

C# 8 supports using-declarations that we can use instead using-blocks. Let’s start with the following code.

class Program
{
    static void Main(string[] args)
    {
        var s = "";

        using(var fileStream = new FileStream("invoices.csv", FileMode.Open))
        using(var reader = new StreamReader(fileStream))
        {
            s = reader.ReadToEnd();
        }

        s = s?.Trim();
    }
}

With using-declarations it’s smaller and cleaner. We don’t need to declare blocks anymore.

class Program
{
    static void Main(string[] args)
    {
        var s = "";

        using var fileStream = new FileStream("invoices.csv", FileMode.Open);
        using var reader = new StreamReader(fileStream);

        s = reader.ReadToEnd();
        s = s?.Trim();
    }
}

When building, removing .pdb and opening the assembly in dotPeek we can see what compiler produced from this code.

class Program
{
    private static void Main(string[] args)
    {
      string str = "";
      using (FileStream fileStream = new FileStream("invoices.csv", FileMode.Open))
      {
        using (StreamReader streamReader = new StreamReader((Stream) fileStream))
          str = streamReader.ReadToEnd()?.Trim();
      }
    }
}

We can see that using-blocks are back and compiler has optimized our code to avoid copies of string.

Compare it to what was produced from using-declarations by C# 8.0 compiler when it was in preview. There are small differences.

Dispose pattern in C#

Those who write classes extending IDisposable interface can use dispose pattern. Let’s start with most primitive example of some disposable class.

public class MyDisposable : IDisposable
{
    public void Dispose()
    {
        // release unmanaged resources
    }
}

This code is open for one problem – what if Dispose() is called multiple times? Depending on unmanaged resources used it can be problematic. Here is the empty implementation of disposable pattern.

public class MyDisposable : IDisposable
{

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~MyDisposable2()
    // {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }
    #endregion

}

Notice that implementation of dispose pattern also gives us hints about how to use finalizer and how to control garbage collection.

Implementing dispose pattern

To implement dispose pattern in class that uses unmanaged resources I will create a dummy class that is actually safe to use and play with. There’s just one unmanaged pointer and list of disposable objects.

public class MyDisposable : IDisposable
{
    private IntPtr? _pointer = IntPtr.Zero;
    private IList<IDisposable> _disposableObjects = new List<IDisposable>();

    private bool disposedValue = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                foreach(var item in _disposableObjects)
                {
                    if(item != null)
                    {
                        item.Dispose();
                    }
                }
            }

            // release _pointer
            _pointer = null;

            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }  
}

Notice how list items are disposed when class is disposing and unmanaged resource is released after this.

Wrapping up

Disposing of objects and IDisposable interface are fundamental concepts of .NET Framework. In C# we have multiple ways to use disposable objects. We can declare and dispose objects manually but we can also use using-blocks or using-declarations. Those who write disposable classes should use dispose pattern to follow same coding style with Microsoft.

Liked this post? Empower your friends by sharing it!
Categories: .NET C#

View Comments (8)

  • Good post thanks. The only issue is Dispose is not async. What would you recommend for asynchronous dispose pattern?

  • Vast majority of BCL classes do have finalizers implemented, that call Dispose. One can reason, this is some kind of a protection from a resource leak. Why in reality it isn't, just like in your example with ADO.NET objects? Assuming the objects get collected, the unmanaged resources should have been released. Unless the objects stayed alive and were not collected...

  • What if you have a class that is using disposables with the using pattern, does that class also need to be disposable?

  • Przemyslaw - I don't know much about internals of ADO.NET classes and when this crap happened it was .NET 1.1 or .NET 2.0 era. Internally ADO.NET uses connection pooling to keep connections ready for reuse. It's possible that not disposing these correctly was conflicting thing that messed up everything. Having call to Dispose() in destructor is sort of safety net to make sure that disposable resources get disposed even when developers forgot to do it.

    Chris - you can go this way but the code fails with dependency injection. These components check if class implements IDisposable or not and if not then they don't dispose instances automatically,

  • Nice article. But you also might want to mention why it is important to only dispose of the managed objects if disposing = true.

  • Hello, How to create a profile for the users in c# the profile only contains the weight for each user?
    thanks a lot

Related Post