Thursday, April 9, 2009

Using Statement in .NET

This blog is aimed at highlighting the best practices to be followed when working with SqlConnection, Stream (FileStream, StreamReader etc) or objects which implement IDisposable interface.

Recently I was going through some of the code developed by some of my colleagues. Many places I saw the try catch and finally block being used. Inside the try block people have written code to open SqlConnection, Stream objects and many other stuffs. Some sample code is given below.

SqlConnection con = new SqlConnection("Some connection string");
try
{
    con.Open();
        /*
        * Other code goes here
        */
        con.Close();
}
catch (Exception ex)
{
    //Exceptiion handling goes here.
}
finally
{
    //Some stupid code written here as well.
}

//Code making use of stream object.

System.IO.FileStream fileStream = new System.IO.FileStream("dummyFile.txt", System.IO.FileMode.Append);
try
{
    fileStream.Write(new UTF8Encoding(true).GetBytes("Dummy Text"), 0, "Dummy text".Length);
        /*
        * Other stuffs go here
        */       
}
catch (Exception ex)
{
    fileStream.Close();
    //Exceptiion handling goes here.
}
finally
{
    //Some stupid code return here as well.
}

The above two e.g. are not the best way to work with a connection/stream object. In the first e.g. the problem is that the connection object is opened and closed inside the try block.
What problem this will cause is that if there is any exception after the connection is opened the runtime will stop any further execution of code and move to the catch block, leaving the connection object open, and then to the finally block. The connection object will remain open as the execution jumped to the catch block. The second e.g. seems better but there is a prob with that as well. The stream object is closed in the catch block which means if there is any exception the stream object will be closed else it will be left open.

Then what is the best way to implement these kind of scenarios? The best thing to do is to close the connection/stream objects in the finally block as shown below.

SqlConnection con = new SqlConnection("Some connection string");
System.IO.FileStream fileStream = new System.IO.FileStream("dummyFile.txt", System.IO.FileMode.Append);
try
{
        con.Open();
        fileStream.Write(new UTF8Encoding(true).GetBytes("Dummy Text"), 0, "Dummy text".Length);
        /*
        * Other code goes here
        */
}
catch (Exception ex)
{
    //Exceptiion handling goes here.
}
finally
{
        // Best way
        con.Close();
        fileStream.Close();
}

The mistakes pointed out can be because of ignorance, or oversight. So what can be done to overcome these pitfalls? The answer to overcome all these shortcoming is to make use of the “using” statement whenever your working with connection/stream objects. Or in other words using statement can be used with any types which implement IDisposable interface. The above codes are modified and shown below.

using (SqlConnection con = new SqlConnection("Some connection string"))
{
    con.Open();
    //Your usual code goes here
}

using (System.IO.FileStream fileStream = new System.IO.FileStream("dummyFile.txt", System.IO.FileMode.Append))
{
    fileStream.Write(new UTF8Encoding(true).GetBytes("Dummy Text"), 0, "Dummy text".Length);
    // Your code goes here
}

What the using statement does is it takes the type which implements IDisposable interface, executes the code between using statement and finally calls the Dispose method of the type. This way the developer need not worry about calling the “Close” method’ of Connection/Stream objects. The system takes care of doing all this.

What happens behind the scene is that the compiler converts the using statement into three parts namely acquisition, usage and disposal. In simple terms acquisition is the part where the compiler tries to acquire the resource, usage is the code between the try block or making use of the acquired resource and disposal is the code in the finally block where the object is disposed by calling the Dispose method. The compiler converts the using statement into a try finally block after compilation. The above SqlConnection object used along with the using statement is converted to something like this by the .NET compiler (The compiler converts the C# code into MSIL, the below code is just a representation of the IL in C# terms).

{
    SqlConnection con = new SqlConnection("Some connection string"); //Acquisition
    try
    {
        con.Open(); //Usage
        //Your usual code goes here
    }
    finally
    {
        ((IDisposable)con).Dispose(); //Disposal
    }
}

Below I have pasted the IL code generated by the C# compiler for the using statement used along with the connection object.

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection con,
        [1] bool CS$4$0000)
    L_0000: nop
    L_0001: ldstr "Some connection string"
    L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
    L_000b: stloc.0
    L_000c: nop
    L_000d: ldloc.0
    L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::Open()
    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
    L_001f: ldloc.0
    L_0020: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0025: nop
    L_0026: endfinally
    L_0027: nop
    L_0028: ret
    .try L_000c to L_0017 finally handler L_0017 to L_0027
}

The C# code for the above compiler generated IL code is as follows.

using (SqlConnection con = new SqlConnection("Some connection string"))
{
    con.Open();
    //Your usual code goes here
}

If you analyse the IL code you can see the last line (.try L_000c to L_0017 finally handler L_0017 to L_0027) which says that there is a try and a finally block from so (L_000c to L_0017) and so (L_0017 to L_0027) lines respectively. We have not made use of the try finally block in our code but the compiler compiles the using statement and converts it to a try and finally block. Also one can notice the absence of the catch block which means the compiler wants to propagate the error to the user and wants the user to handle it appropriately. If you see line no L_0020 the compiler has embedded the IL statement to call the Dispose method.

The using statement makes sure that the Dispose method of the object is called even if there is an error. The reason why this happens, is obvious from the IL code. The Dispose method gets called in the finally part.

So the lesson learnt from this is that wherever in the code IDisposable interface implemented objects are used please refactor your code to make use of the using statement. Some e.g. of objects implementing IDisposable interface are SqlDataReader, SqlTransaction, SqlCommand,  DataColumn, DataSet, DataTable, DataView, DataAdapter, BinaryReader, BinaryWriter, FileStream, MemoryStream, StreamReader, StreamWriter, StringReader, Font, FontFamily, BitMap and many other classes.

Small note on the working of the try, catch and finally block.

Whenever I ask candidates during an interview to state the flow of the try, catch and finally block, many of them just don’t get it right. So this small note tries to shed some light on the same. If there is an exception in your try block the execution gets transferred to the catch block and once the catch block is executed the control shifts to the finally block (if there is any). And if there is no error in the try block the execution gets transferred to the finally block i.e. finally block executes irrespective of whether there is an error or not in the try block. If due to some stupid code written in the catch block, an exception is raised in the catch block, then the control gets transferred to the finally block. So whatever happens if the code execution enters the try block, irrespective of the exception inside the try block, finally block is executed even if there is an exception in the catch block.

Try to know more,

Sandeep.

3 comments:

  1. Is it Possible to use Using Statement in VB.net 2003?

    ReplyDelete
  2. Hi Priyan,
    I am afraid its not possible. Using statement was introduced in VB.NET 8.0 i.e. in VS 2005.

    ReplyDelete

Please provide your valuable comments.