Desugaring the using statement

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:

   1:  public interface IDisposable
   2:  {
   3:      void Dispose();
   4:  }

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:

   1:  IDisposable disposableFile = File.CreateText("temp.txt");
   2:  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:

   1:  IDisposable disposableFile = null;
   2:  try
   3:  {
   4:      disposableFile = File.CreateText("temp.txt");
   5:  }
   6:  finally
   7:  {
   8:      disposableFile.Dispose();
   9:  }

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:

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

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

   1:  .method private hidebysig static void  Main(string[] args) cil managed
   2:  {
   3:    .entrypoint
   4:    // Code size       34 (0x22)
   5:    .maxstack  2
   6:    .locals init ([0] class [mscorlib]System.IO.StreamWriter disposableFile,
   7:             [1] bool CS$4$0000)
   8:    IL_0000:  nop
   9:    IL_0001:  ldstr      "temp.txt"
  10:    IL_0006:  call       class [mscorlib]System.IO.StreamWriter  
[mscorlib]System.IO.File::CreateText(string)
  11:    IL_000b:  stloc.0
  12:    .try
  13:    {
  14:      IL_000c:  nop
  15:      IL_000d:  nop
  16:      IL_000e:  leave.s    IL_0020
  17:    }  // end .try
  18:    finally
  19:    {
  20:      IL_0010:  ldloc.0
  21:      IL_0011:  ldnull
  22:      IL_0012:  ceq
  23:      IL_0014:  stloc.1
  24:      IL_0015:  ldloc.1
  25:      IL_0016:  brtrue.s   IL_001f
  26:      IL_0018:  ldloc.0
  27:      IL_0019:  callvirt   instance void [mscorlib]                      System.IDisposable::Dispose()
  28:      IL_001e:  nop
  29:      IL_001f:  endfinally
  30:    }  // end handler
  31:    IL_0020:  nop
  32:    IL_0021:  ret
  33:  } // 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!

Wouter de Kort works as a lead architect and consultant. He helps organizations stay on the cutting edge of software development. Wouter focuses on DevOps. He loves solving complex problems and helping other developers to grow. Wouter authored the book DevOps on the Microsoft stack and a couple of other books. Wouter is a Microsoft MVP and an ALM Ranger. You can find him on Twitter (@wouterdekort), on his blog at wouterdekort.com and at the various conferences where Wouter speaks.

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.