Desugaring the using statement

2013-09-30

Do you need to worry about memory management in .NET?

Although .NET is a managed environment with a garbage collector, this doesn’t mean you don’t have to worry about memory management. Of course, it’s different then when using a language like C++ where you have to explicitly free memory but in C# you still have to think about memory management.

The garbage collector in .NET is a big help in freeing memory and when you were only dealing with managed objects, that would be enough. However, in most applications you don’t only deal with managed objects. Maybe you access a file on disk, a web service or a database. Those resources are unmanaged and they do have to be freed explicitly.

If you won’t do anything, the garbage collector will eventually kick in and remove unused objects from memory. The classes in the .NET Framework that deal with unmanaged resources implement what’s called a finalizer. The finalizer runs code to close your file or database handle. So with a well-designed class, the finalizer will eventually cleanup your unmanaged resources. But waiting for the garbage collector makes your code unreliable because you can’t tell for sure when an external handle will be closed.

Meet IDisposable

To cleanup memory in a deterministic way, the .NET framework offers the IDisposable interface. This is interface is pretty simple:

public interface IDisposable
{
    void Dispose();
}

The interface has only one method called Dispose. Calling this method will free any unmanaged resources that an object has. This way you can explicitly determine when a resource should be freed. A lot of the types in the .NET framework implement IDisposable. For example, when you create a new text file you get back a StreamWriter that implements IDisposable:


IDisposable disposableFile = File.CreateText("temp.txt");
disposableFile.Dispose();

When dealing with disposable objects you should call Dispose as soon as possible. But what if an exception happens before you can call Dispose? To make sure that some code always runs, with or without an exception, C# offers you the finally block:

IDisposable disposableFile = null;
try
{
    disposableFile = File.CreateText("temp.txt");
}
finally
{
    disposableFile.Dispose();
}

If some exception happens in the try block, the Dispose method will always be called. This way you can make sure that your resources will be released. Writing a try/finally block every time you deal with an IDisposable quickly becomes cumbersome. Fortunately, C# offers some syntactic sugar that can help you.

Desugaring using

When dealing with an IDisposable object, you can use a special statement called the using statement. The previous code with the try/finally block can be changed into the following:


using (var disposableFile = File.CreateText("temp.txt"))
{
    // do something with your file
}

This is a much nicer syntax for working with disposable objects. When you look at the IL this generates you will see the following:


method private hidebysig static void  Main(string\[\] args) cil managed
{
    .entrypoint
    // Code size       34 (0x22)
    .maxstack  2
    .locals init (\[0\] class \[mscorlib\]System.IO.StreamWriter disposableFile, [1] bool CS$4$0000)
    IL_0000:  nop
    IL_0001:  ldstr      "temp.txt"
    IL_0006:  call       class[mscorlib]System.IO.StreamWriter [mscorlib]System.IO.File::CreateText(string)
    IL_000b:  stloc.0

   .try
   {
      IL_000c:  nop
      IL_000d:  nop
      IL_000e:  leave.s    IL_0020
   }  // end .try
   finally
   {
      IL_0010:  ldloc.0
      IL_0011:  ldnull
      IL_0012:  ceq
      IL_0014:  stloc.1
      IL_0015:  ldloc.1
      IL_0016:  brtrue.s   IL_001f
      IL_0018:  ldloc.0
      IL_0019:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
      IL_001e:  nop
      IL_001f:  endfinally
     }  // end handler

    IL_0020:  nop
    IL_0021:  ret
} // end of method Program::Main

As you can see, your small using statement generates quite a bunch of IL code. When you look at line 12 and 18 you see the try and finally statements. And in the finally block you see a call to Dispose on the StreamWriter.

And that’s how you can easily work with unmanaged objects in C#. Make sure that you always dispose of objects that implement IDisposable. The using statement is the easiest way to do this and by desugaring it you now understand why.

And if you ever find yourself creating a class that uses unmanaged resources, think of IDisposable and a finalizer.

Feedback? Questions? Please leave a comment!