understanding ‘using’ block in C#
using block in C# comes very handly while dealing with disposable objects. Disposable objects are those objects that can explicitly release the resources they use when called to dispose. As we know .Net garbage collection is non-deterministic so you can’t predict when exactly the object will be garbage collected. Many times we need to make sure the object is disposed along with all the resources it uses immediately after the purpose is served. This becomes possible with using block. using block in C# lets you deal with disposable objects effectively. But how does it work ? How does using block ensure the object is immediately disposed after the scope of the using block?
Lets look at what MSDN has to say. As per the msdn
using block Defines a scope, outside of which an object or objects will be disposed.
The using statement allows the programmer to specify when objects that use resources should release them. The object provided to the using statement must implement the IDisposable interface. This interface provides the Dispose method, which should release the object’s resources.
Surprisingly even MSDN does not clarify how does this happen under the hood. It only says the object has to implement IDisposable interface that provides Dispose method on the object implementing the interface. So to dispose the object it will need to call the Dispose method on the object which will cleanup and release the resources used by the object.
This led me to write some code and analyze the generated IL where I found the answers. Have a look at this code. I’ve written a class Car and made it IDisposable so that I can club its’ lifetime with using block.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogSamples
{
public class Car : IDisposable
{
public Car(int id)
{
this.Id = id;
}
public int Id { get; set; }
public void Run()
{
Console.WriteLine("Car {0} is running.", this.Id.ToString());
}
#region IDisposable Members
public void Dispose()
{
Console.WriteLine("Releasing resources used by Car object : " + this.Id.ToString());
}
#endregion
}
}
Here’s my client-code. A sample ConsoleApplication that uses the Car object within the using block.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogSamples
{
class Program
{
static void Main(string[] args)
{
using (Car myCar = new Car(1))
{
myCar.Run();
}
}
}
}
The output was obvious. Immediately after the using block the Dispose method of the Car object is called. Note that there are no signs of Dispose() method being called explicitly, still the method is called. Lets look at the generated IL.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 37 (0x25)
.maxstack 2
.locals init ([0] class BlogSamples.Car myCar,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: newobj instance void BlogSamples.Car::.ctor(int32)
IL_0007: stloc.0
.try
{
IL_0008: nop
IL_0009: ldloc.0
IL_000a: callvirt instance void BlogSamples.Car::Run()
IL_000f: nop
IL_0010: nop
IL_0011: leave.s IL_0023
} // end .try
finally
{
IL_0013: ldloc.0
IL_0014: ldnull
IL_0015: ceq
IL_0017: stloc.1
IL_0018: ldloc.1
IL_0019: brtrue.s IL_0022
IL_001b: ldloc.0
IL_001c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0021: nop
IL_0022: endfinally
} // end handler
IL_0023: nop
IL_0024: ret
} // end of method Program::Main
Do you spot that compiler has introduced try/finally block and replaced it with using block. But why did compiler do that ? Lets observe the code and attempt to write similar C# code for better understanding. After careful analysis and reverse-engineering the equivalent C# code would look something as following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogSamples
{
class Program
{
static void Main(string[] args)
{
//Declare myCar object with FullName of the type as seen in IL.
BlogSamples.Car myCar;
//Instantiate the object by calling the constructor, matching the flow of IL.
myCar = new Car(1);
try
{
myCar.Run();
}
finally
{
if(myCar != null)
myCar.Dispose();
}
}
}
}
Above code is very clear. All it does is replace the code within the using block into the try block and calls the Dispose() method of the object in the finally block. As we all know, the code written in finally block gets executed in any situation even in case of any handled or un-handled exception. And this is how the using block ensures that the objects used with the using block are disposed for sure.
In fact using block is just a syntactic sugar for this pattern of code.
Some of you might wonder, how does the generated IL of the new reverse-engineered code look like. The IL of the reverse-engineered looks like this.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 31 (0x1f)
.maxstack 2
.locals init ([0] class BlogSamples.Car myCar)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: newobj instance void BlogSamples.Car::.ctor(int32)
IL_0007: stloc.0
.try
{
IL_0008: nop
IL_0009: ldloc.0
IL_000a: callvirt instance void BlogSamples.Car::Run()
IL_000f: nop
IL_0010: nop
IL_0011: leave.s IL_001d
} // end .try
finally
{
IL_0013: nop
IL_0014: ldloc.0
IL_0015: callvirt instance void BlogSamples.Car::Dispose()
IL_001a: nop
IL_001b: nop
IL_001c: endfinally
} // end handler
IL_001d: nop
IL_001e: ret
} // end of method Program::Main
Needless to mention that the IL in both the cases is identical to each other which proves the point that the using block is nothing but a syntactic sugar.
You might ask what if the constructor throws an exception. In this case the control would not move forward any further and never enter the proceeding try block. The object myCar still remains null. However you can wrap the using block in a try/catch block as shown below and handle the situation gracefully.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogSamples
{
class Program
{
static void Main(string[] args)
{
try
{
using (Car myCar = new Car(1))
{
myCar.Run();
}
}
catch
{
//Handle the situation gracefully.
}
}
}
}

simply good one
Comment by pranay rana on July 28, 2010 at 10:41 PM