Overview
The full value of the C# using statement is often underrated.
This article will describe what this statement does and why you should use it often.
Note that this article is discussing the using statement in the
context of defining object scope and disposal, not of namespace inclusion (i.e. not "using System.Data;").
The Common Problem
Although a managed language like C# typically handles managing memory and
other resources on your behalf there are still certain objects you need to be careful with. One of these
that almost any web programmer is aware of is a database connection object. Since these objects
are often pooled and therefore not immediately destroyed if you're not careful to clean up the
resources they reference you'll have problems. Forget to call the Close
method after calling the Open method and you're just asking for trouble.
This is where the using statement comes in. In this article I'm going to
make the bold suggestion that you never call the Close method of
a database connection method. Suicidal? We'll see...
Typical Safety
Before I back up my suggestion let's first take a look at how we usually ensure that our
resource critical objects are properly cleaned up. It's normal practice to first write code that
achieves it's primary purpose. For instance if we were coding a data fetch we'd probably
code something like:
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = connection.CreateCommand();
command.CommandText = "mysp_GetValue";
command.CommandType = CommandType.StoredProcedure;
connection.Open();
object ret = command.ExecuteScalar();
connection.Close();
In this case we've correctly released the connection when we're done with it. However what if
something goes wrong while executing the query? A big fat exception will be thrown and the
Close method will never be called. If this code is in a web page
every time someone hits the page we're going to open another connection to the database. If
you've never had the joy of this happen to you on a busy production server you're really missing
out my friend.
Due to the plethora of database connection code samples out there almost everyone knows the "proper" way to do this:
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = connection.CreateCommand();
command.CommandText = "mysp_GetValue";
command.CommandType = CommandType.StoredProcedure;
object ret = null;
try
{
connection.Open();
ret = command.ExecuteScalar();
}
finally
{
if (connection != null)
connection.Close();
}
There, we've now done our duty and the code is now safe. Regardless of any pesky exceptions the SqlConnection
will get closed after it's been opened. We couldn't do any better, right?
Well, I'm afraid I only give the above code a "B" grade. One of the reasons for this is because it's
not fool-proof. A good coder codes defensively, not only for how the code currently runs but also for what
code happen when the code is later adjusted (the code is always adjusted). Suppose some clever coder comes along
and does this:
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = connection.CreateCommand();
command.CommandText = "mysp_GetValue";
command.CommandType = CommandType.StoredProcedure;
object ret = null;
try
{
connection.Open();
ret = command.ExecuteScalar();
}
finally
{
EmailerClass.EmailMe("ret = " + ret.ToString() + "!!!");
if (connection != null)
connection.Close();
}
The poor coder has done his best to add some functionality he told his boss would be very easy to do - "it
will only take 5 seconds". However what happens when ret is null or
something goes wrong in the EmailerClass.EmailMe method which of course doesn't handle it's own exceptions?
An exception is thrown that prevents the connection from being closed.
Using Using
The answer to these problems is the using statement. This statement guarantees
that resources are properly cleaned up and that they're cleaned up in the most timely manner. Recoded with
the using statement our code block would look like:
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandText = "mysp_GetValue";
command.CommandType = CommandType.StoredProcedure;
connection.Open();
object ret = command.ExecuteScalar();
}
The big change here is that we're wrapping the instantiation of our resource critical object
in the "resource-acquisition" part of a using statement. The curly braces
after this define the scope of the object. When the object goes out of scope it is automatically
cleaned up. In the case of our SqlConnection object, it's connection will be closed. This is true
regardless of any exception that gets thrown. So you don't have to worry about other programmers
not being careful about what code they inject and where they inject it. The code that you wrote is safe.
There you have it - a database connection object being used with no call being made to it's
Close method. If you're interested in how this "magic" works and what the performance
implications are, read on. Otherwise just start coding with using statements.
IDisposable
So how does our using statement, as coded above, know that for the particular
object involved (the database connection object) the connection should be closed when the object goes out of
scope? The answer is that this is done in the usual object-oriented way - via the implementation of
an interface. The interface for this is called IDisposable and it is important
enough to be found in the System namespace.
When a class implements the IDisposable interface it must implement
a method called Dispose. It is this method that gets called when an object
goes out of the scope of a using statement. In effect the above
code is functionally equivalent to:
IDisposable connection = null;
try
{
connection = new SqlConnection(connectionString);
SqlCommand command = connection.CreateCommand();
command.CommandText = "mysp_GetValue";
command.CommandType = CommandType.StoredProcedure;
((SqlConnection)connection).Open();
object ret = command.ExecuteScalar();
}
finally
{
connection.Dispose();
}
This implies that only objects that implement IDisposable can be used
in a using statement, which is important to note. It is also important to note
that calling Dispose explicitly (or implicitly via a using
statement) can have performance benefits. As MSDN states: "Code that is using a resource can call Dispose to indicate
that the resource is no longer needed. If Dispose is not called, then automatic disposal eventually occurs as a
consequence of garbage collection." Yet another reason to use using statements.
But "wait a minute" you say! "What if I need my object to be a class member and therefore
can't wrap it's scope in a using statement?" Good question! The answer to is to
have your class implement IDisposable and in the Dispose
method call the Dispose method of your member object. Then whenever you instantiate
your class, do so in a using statement.