chris carter's web log

Home |  Contact |  Admin
 

Try..Catch...Now What

Posted on September 14, 2007

Grrrrr.  This is a little bit of a rant.  I don't get it.  I'm working on a project right now that has code like this:

public SqlConnection GetYourFavoriteConnection()
{
  SqlConnection Conn;
  try
  {
    Conn = new SqlConnection(connString);
    Conn.Open();
  }
  catch (Exception e)
  {
    throw new Exception(e.Message, e);
  }
  return Conn;
}

Just to be clear, I'm focusing on the use of the try...catch block only.  The project I'm working on is riddled with this shit code.  Why is it shit code? Well, what was wrong with the original exception? Not good enough for your code?? Not as cool as a "new" Exception?? Am I missing something?

My Beef

Where's the value add? What's the point of catching an exception if you're not going to do anything with it?  Here's another good one,

SqlConnection Conn;
try
{
  //do stuff with Conn
}
catch(Exception e)
{
  throw new Exception(e.Message, e);
}
finally
{
  if (Conn != null)
  {
    Conn.Close();
  }
}

I've blogged about this before and apparently I'm blogging about it now. The using statement will do heavy lifting for you.  The above could be rewritten like this:

using (SqlConnection Conn = new SqlConnection("connection string"))
{
  //do stuff with Conn here
}

What's So Special About 'Using'??

The following code:

using System;
using System.Data.SqlClient;
namespace ConsoleApplication1
{
  public class Program
  {
    public static void Main(string[] args)
    {
      using (SqlConnection cn = new SqlConnection("connection string"))
      {     	
      }
    }
  }
}

...produces this IL

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       34 (0x22)
  .maxstack  2
  .locals init ([0] class [System.Data]System.Data.SqlClient.SqlConnection cn,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "connection string"
  IL_0006:  newobj     instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
  IL_000b:  stloc.0
  .try
  {
    IL_000c:  nop
    IL_000d:  nop
    IL_000e:  leave.s    IL_0020
  }  // end .try
  finally
  {
    IL_0010:  ldloc.0
    IL_0011:  ldnull
    IL_0012:  ceq
    IL_0014:  stloc.1
    IL_0015:  ldloc.1
    IL_0016:  brtrue.s   IL_001f
    IL_0018:  ldloc.0
    IL_0019:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_001e:  nop
    IL_001f:  endfinally
  }  // end handler
  IL_0020:  nop
  IL_0021:  ret
} // end of method Program::Main

Notice anything?? Like the try..finally?? Ya, the using statement will put those in for you.

It Calls Dispose For You, So What?

Well for one that means less code to write.  Read here for the other.  For those who do not trust the documentation here's the code for Dispose as seen through Reflector:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        this._userConnectionOptions = null;
        this._poolGroup = null;
        this.Close();
    }
    this.DisposeMe(disposing);
    base.Dispose(disposing);
}

Take note of the fact that it calls Close for you. No need to code for it...

kick it on DotNetKicks.com

Comments

FrankC

What's interesting is that if you look into the source code that Microsoft provides in the Enterprise Libraries, starter kits, and code samples, both in C# and VB.NET, you'll see code like you're talking about there too.

The thing about Close is interesting. Do you know if this should happen for all classes with the IDisposable interface? It seems like it should but I've seen some odd behavior with some objects when Close wasn't called even though a Using block was there.

Chris

Hmmm... "should" is tricky verbage. This brings up a good point though. I know that it's superfluous to add the try...finally around the connection usage, but really, this is in hind site. Meaning, had I not seen whatever it was that informed me of the whole "...calling dispose results in calling close...." blah blah blah .... I guess my point is this, the assumption is that calling dispose, will handle cleaning up of any resources that may be out of your control. Should it be a requirement that anyone with a 'Close' method be required to call that method within dispose? As of the time of this writing, I say no, but I reserve the right to change my mind pending any other information :)

Joe

"Where's the value add? What's the point of catching an exception if you're not going to do anything with it? Here's another good one"

The only thing I can think, and I see this a lot (a long with other useless crap in the catch block like string x = e.Message) is that it provides a place for a breakpoint...

Chris

@Joe, that's another good one "string x = e.Message", I just saw this one this morning, good grief:
public static int SomeMethod(){
try{
*snip*
}
catch (Exception e){
result = -999;
throw e;
}
}

tom

look up this:

try
{
[ something iffy ]
}
catch
{
throw; <-- note minimal syntax.
}

Chris

try
{
[ something iffy ]
}
catch
{
throw; <-- note minimal syntax.
}

@tom, even though the code is catching the exception and re-throwing the original exception, there's still no value add, why catch it in the first place?

Will Asrari

I had a "team member" give me static for not explicitly calling "connection.Close()" when I had my connection in a using-construct.

Needless to say, I don't work for that company anymore.


using (SqlConnection connection = GetConnection())
using (SqlCommand command = new SqlCommand("command", connection))
{
connection.Open();
cm.CommandType = commandType;

using (IDataReader reader = command.ExecuteReader(..))
while (reader.Read())
}


I feel that when I do use a try-catch it is for a specific exception (i.e. SqlException).

I just lol'ed @ "shit code"

Chris

"I feel that when I do use a try-catch it is for a specific exception (i.e. SqlException)."

Yep, and if you catch a SqlException you'll probably be logging the sql that caused that exception, and/or rolling back the transaction if you're in one, or any number of things that would be a value add to catching the exception in the first place.

Nate

"@tom, even though the code is catching the exception and re-throwing the original exception, there's still no value add, why catch it in the first place?"

I'm not Tom but it makes debugging a hell of a lot easier. You can place a breakpoint there and see your vars, etc.

Chris

What the-

Who the-

breakpoint??

why would you need a breakpoint on a new exception?

Why not just break on the violator who threw the original exception?

Member Blogs

.NET Using strings and enum - C# .NET Using .NET Custom Attributes for release documentation Book: Announcing

Ian

I'm not sure if things have changed since ASP.NET 1.1, but there is an issue with nested 'using' statements. What would happen is, if a nested disposable object throws an exception then the outter objects wouldn't get disposed.

Also, doesn't each 'using' statement create it's own try/catch statement meaning there are 2 for every connection? If my assumptions are correct then it's more efficient to write your own try/catch statement, albiet less readable.

Chris

Hmmm, not sure about a problem with nested usings, hadn't heard of one but that'd be good info.

As for each using generating it's own try/finally, that's to ensure that whatever object supports IDisposable is guaranteed to have it's own Dispose called, so a single try/catch around a connection and a command would need to ensure that Dispose is called on both the Command and the Connection.

More efficient? I have no clue either way. More readable? yep, I'll stick with more readable until .NET 5.0 comes out and the nested using's become the new "shit code" :)

Ian

Oops, I made a mistake. I misread the passage from "Effective C#" by Bill Wager that covers this topic. The bug I describe only happens if you do:


using (myConnection as IDisposable)
using (myCommand as IDisposable)
{ ... }


My appologies. However, the book confirmed that:


using (SqlConnection myConnection = new SqlConnection( ... ))
using (SqlCommand myCommand = new SqlCommand( ... ))
{ ... }


results in IL equivilent to:


try
{
SqlConnection myConnection = new SqlConnection( ... );
try
{
SqlCommand myCommand = new SqlCommand( ... );
...
}
finally
{
if(myCommand != null)
myCommand.Dispose();
}
}
finally
{
if(myConnection != null)
myConnection.Dispose();
}


Thats pretty ugly but you're right, now I think about it I doubt it would incure much of a performance hit.

Anyways, thanks for the reply and the great blog!

Post a Comment

(required)
(required)
(no HTML!)