Tom Fischer

  Home  |   Contact  |   Syndication    |   Login
  21 Posts | 0 Stories | 44 Comments | 0 Trackbacks

News

Archives

Post Categories

.NET Framwork

TFS

Tools

Visual Studio

Using() Statement in 3 seconds and a bug in Reflector

The boring, known accross the board definition from the MSDN site:
Defines a scope, outside of which an object or objects will be disposed

The more interesting definition from Tom:
The using() Statement generates a try{} finally{ //virtual call of the dispose method } block with null check!

Let's take a deeper look.

Let's write two test methods first:

//Testmethod for Using   
public static void UsingTest()
{
    using (SqlConnection con = new SqlConnection())
    {
        con.ConnectionString = "Test Connection";
    }
}

//TestMethod for try finally
public static void TryFinallytest()
{
    SqlConnection con = new SqlConnection();
    try
    {
        con.ConnectionString = "Test Connection";
    }
    finally
    {
        con.Dispose();
    }
}

Note: The TryFinallyTest() method has no null check inside the finally block. This means in case con is null the function would throw a Null Reference Exception. The UsingTest() method does not.

When you compile both methods and look at them disambeled in Reflector they both look exatly the same:

public static void UsingTest()
{
    using (SqlConnection con = new SqlConnection())
    {
        con.ConnectionString = "Test Connection";
    }
}

public static void TryFinallytest()
{
    using (SqlConnection con = new SqlConnection())
    {
        con.ConnectionString = "Test Connection";
    }
}

But looking at the IL (see below) reveals a very important difference:
-  Frist both get translated into a try{} finally {} block. (see IL code below). This is what we expected
The using(){} statement adds a null check before calling the dispose method inside the finally.
-  The compiler did not add a null check for the TryFinallyTest method (as intented)

The Problem:
The TryFinallyTest method gets disasembeld from IL into C# (or VB) by using a using() statement .
This is not correct because a using() statement has a null check before calling the dispose() method.
This means the disasembled C# code does not relfect the IL correctly.

Here is the IL code for the UsingTest() method:

.method public hidebysig static void UsingTest() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection con,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor()
    L_0006: stloc.0 
    L_0007: nop 
    L_0008: ldloc.0 
    L_0009: ldstr "Test Connection"
    L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::set_ConnectionString(string)
    L_0013: nop 
    L_0014: nop 
    L_0015: leave.s L_0027
    L_0017: ldloc.0     
    L_0018: ldnull  
    L_0019: ceq 
    L_001b: stloc.1 
    L_001c: ldloc.1 
    L_001d: brtrue.s L_0026    //Nullcheck
    L_001f: ldloc.0 
    L_0020: callvirt instance void [mscorlib]System.IDisposable::Dispose()  //dispose call
    L_0025: nop 
    L_0026: endfinally 
    L_0027: nop 
    L_0028: ret 
    .try L_0007 to L_0017 finally handler L_0017 to L_0027
}

Note: L_001d: brtrue.s perfoms the null check. In case con is null it jumps directly to the endfinally. This means in case con is null there is no Null Reference Exception

Here is the IL code for the TryFinallyTest method:

.method public hidebysig static void TryFinallytest() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection con)
    L_0000: nop 
    L_0001: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor()
    L_0006: stloc.0 
    L_0007: nop 
    L_0008: ldloc.0 
    L_0009: ldstr "Test Connection"
    L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::set_ConnectionString(string)
    L_0013: nop 
    L_0014: nop 
    L_0015: leave.s L_0021
    L_0017: nop 
    L_0018: ldloc.0   //it loads con and calls directly dispose in the next line. No null check
    L_0019: callvirt instance void [System]System.ComponentModel.Component::Dispose()
    L_001e: nop 
    L_001f: nop 
    L_0020: endfinally 
    L_0021: nop 
    L_0022: ret 
    .try L_0007 to L_0017 finally handler L_0017 to L_0021
}

Note: Inside the the finally block the IL loads con on the Excution Stack and calls dispose() directly without a null check. In case con is null, an Null Reference Exception is thrown.

 

So, what to use? Using() or try{} finally{}?
Well, usually I would recommend using Using(), so you don't have to remember the null check.
But if you have multiple using inside eachother I would rather use one try{} finally{}.

If you don't know (or if you are too lazy to check)  if your object implements IDisposable you can always use the following pattern to be on the save side:

public static void ObjectAsDispose(SqlConnection con)
{
    using (con as IDisposable)
    {
        con.ConnectionString = "Test Connection";
    }
}

I hope you enjoyed this one again.

Tom

P.S.: Thanks Ian for reminding me to add the safty pattern for using!

posted on Monday, September 15, 2008 4:03 PM

Feedback

# re: C# and the using Statement in 3 seconds and a bug in Reflector 9/16/2008 11:02 AM Randolpho
Great post, thanks for the info.

Regarding multiple using statements, there are patterns you can use to keep them all in the same using/disposal block:

// multiple usings, disparate types:
using(var myType1 = new MyType1())
using(var myType2 = new MyType2())
using(var myType3 = new MyType3())
{
myType1.DoWork();
myType2.DoOtherWork();
myType3.DoMoreWork();
}

// multiple usings, homogeneous types:
using(MyType myType1 = new MyType(), myType2 = new MyType())
{
myType1.DoWork();
myType2.DoWork();
}



I personally find those patterns better than a try/finally, but your mileage may vary. :)

# re: C# and the using Statement in 3 seconds and a bug in Reflector 9/17/2008 1:49 PM rusty
I like that you showed the IL, which always seems foreign to me. Maybe I will start 'reading' IL to get more familiar with what it bakes down into.

rusty

# re: C# and the using Statement in 3 seconds and a bug in Reflector 2/2/2009 3:12 PM ILIA BROUDNO
Let me add a comment regarding finally.
It is hard to believe, but it actually does not execute when your method re-throws an exception unless the consumer of your method catches it.
I tested the following C# code built in VS 2005:
static void Main(string[] args)
{
try
{
Console.WriteLine("try");
throw new Exception();
}
finally
{
Console.WriteLine("finally");
}
Console.ReadLine();
}
finally does not execute even if you add catch that re-throws
catch { Console.WriteLine("catch"); throw; }
Is this a mojor bug or what?

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: