views:

684

answers:

10

I understand how try-catch works and how try-finally works, but I find myself using those (usually) in two completely different scenarios:

  • try-finally (or using in C# and VB) is mostly used around some medium-sized code block that uses some resource that needs to be disposed properly.
  • try-catch is mostly used either
    • around a single statement that can fail in a very specific way or
    • (as a catch-all) at a very high level of the application, usually directly below some user interface action.

In my experience, cases where a try-catch-finally would be appropriate, i.e., where the block in which I want to catch some particular exception is exactly the same block in which I use some disposable resource, are extremely rare. Yet, the language designers of C#, VB and Java seem to consider this to be a highly common scenario; the VB designers even think about adding catch to using.

Am I missing something? Or am I just overly pedantic with my restrictive use of try-catch?


EDIT: To clarify: My code usually looks like this (functions unrolled for clarity):

Try
    do something
    Aquire Resource (e.g. get DB connection)
    Try 
        do something
        Try
            do something that can fail
        Catch SomeException
            handle expected error
        do something else... 
    Finally 
        Close Resource (e.g. close DB connection)
    do something
Catch all
    handle unexpected errors

which just seems to make much more sense than putting any of the two catches on the same level as finally just to avoid indentation.

+1  A: 

I would nearly always use try-catch-finaly in cases where you need to dispose something in all cases and you use the case to log the error and/or inform the user.

chrissie1
+12  A: 

A quote from MSDN

A common usage of catch and finally together is to obtain and use resources in a try block, deal with exceptional circumstances in a catch block, and release the resources in the finally block.

So to make it even more clear, think of the code that you want to run, in 99% of the cases it runs perfectly well but somewhere in the chunk there might occure an error, you don't know where and the resources created are expensive.

In order to be 100% sure that the resources are disposed of, you use the finally block, however, you want to pin-point that 1% of cases where the error occures, therefore you might want to set up logging in the catch-ing-section.

That's a very common scenario.

Edit - A Practical Example

There is some good examples here: SQL Transactions with SqlTransaction Class. This is just one of the many ways to use Try, Catch & Finally and it demonstrates it very well, even though a using(var x = new SqlTranscation) might be efficient some times.

So here goes.

var connection = new SqlConnection();

var command = new SqlCommand();

var transaction = connection.BeginTransaction();

command.Connection = connection;
command.Transaction = transaction;

Here comes the more interesting parts

///
/// Try to drop a database
///
try
{
    connection.Open();

    command.CommandText = "drop database Nothwind";

    command.ExecuteNonQuery();
}

So let's imagine that the above fails for some reason and an exception is thrown

///
/// Due to insufficient priviligies we couldn't do it, an exception was thrown
///
catch(Exception ex)
{
    transaction.Rollback();
}

The transaction will be rolled back! Remember, changes you made to objects inside the try/catch will not be rolled back, not even if you nest it inside a using!

///
/// Clean up the resources
///
finally
{

    connection.Close();
    transaction = null;
    command = null;
    connection = null;
}

Now the resources are cleaned up!

Filip Ekberg
Thanks, but then I ask myself: Why would I want to only log the exceptions occuring between obtaining and releasing the resource? That's exactly my point: The block of code where I want to do logging (or any other kind of exception handling) is usually not exactly the same as the block of code where I use one specific resource.
Heinzi
You might want to do a Transaction Rollback in the catch Block and release the resources in the finally block.
Filip Ekberg
Transaction: Thanks, that's exactly the kind of example I was looking for -- one where the lifetime of the resource exactly matches the desired scope of the error handling. +1, and it would be nice if you could add that example to your answer.
Heinzi
I've added an example above, I hope it's sufficient, please tell me if there's something more you are wondering about :)
Filip Ekberg
Great, thanks. (I guess there should be `transaction.Commit()` at the end of the try block.)
Heinzi
I think standard is AutoCommit actually. But yes, add that to be certain
Filip Ekberg
+5  A: 

I often write code that looks like this:

Handle h = ...
try {
   // lots of statements that use handle
} catch (SomeException ex) {
   // report exception, wrap it in another one, etc
} catch (SomeOtherException ex) {
   // ...
} finally {
   h.close();
}

So maybe you are just being overly pedantic ... e.g. by putting a try / catch around individual statements. (Sometimes it is necessary, but in my experience you don't usually need to be so fine-grained.)

Stephen C
+10  A: 

Example:

Try
   Get DB connection from pool
   Insert value in table
   do something else...
Catch
   I have an error... e.g.: table already contained the row I was adding
   or maybe I couldn't get the Connection at all???
Finally
   if DB connection is not null, close it.

You can't really get a more "common" example. Pity that some people still forget to put the close connection in the Finally, where it belongs, instead of in the Try block... :(

p.marino
It's exactly this example that I do not understand: Why would I put the Catch block for the Insert at this place rather than around the Insert statement? Doesn't that pose the risk of accidentally catching an exception that I did not want to catch?
Heinzi
the point I was trying to illustrate is that FINALLY *always* get executed, with or without exception being raised. Therefore move the resource release there. This makes things more self-contained. And your way of doing things means having a catch for opening the connection, one for the write... and possibly more for the "do something else" part which could still raise various exception.And you will have to remember to close the connection in all these parts. Risking to forget it and have resource leaks which are pretty hard to pinpoint.
p.marino
Sorry, but this just does not make sense. I know that *finally* always gets executed, which is why I put resource release there. Can you provide a concrete example of how "my way" of doing things can lead to a resource not being released?
Heinzi
I am surely being over-pedantic, especially if we are talking of DB connections when we say "resource"... but there is a chance that in my pseudocode above, "Get DB connection from Pool" could cause an exception after having acquired the resource. I'd feel safer by checking for that and release in the outer block.
p.marino
A: 

In my experience, cases where a try-catch-finally would be appropriate, i.e., where the block in which I want to catch some particular exception is exactly the same block in which I use some disposable resource, are extremely rare. Yet, the language designers of C#, VB and Java seem to consider this to be a highly common scenario; the VB designers even think about adding catch to using.

you:

try {
   //use resource
} catch (FirstException e) {
   // dispose resource
   // log first exception
} catch (SecondException e) {
   // dispose resource
   // log first exception
} catch (ThirdException e) {
   // dispose resource
   // log first exception
}

me:

try {
  //use resource
} catch (FirstException e) {
  // log first exception
} catch (SecondException e) {
  // log first exception
} catch (ThirdException e) {
  // log first exception
} finally {
  // dispose resource
}

feel defference?)

dart
And the most important difference is that the first version of the code is *broken* if `//use resource` throws some other (e.g. unchecked) exception.
Stephen C
thanks Stephen, I missed that.)
dart
I don't think that's accurate - it sounds like Heinzi would have a try-catch at a high level in the application to log exceptions and a try-finally where you have your try-catch-catch-catch-finally. Heinzi's approach seems appropriate to me.
kvb
+3  A: 

There is nothing wrong with nesting try/catch/finally blocks. I actually use this quite often. I.e. when I use some resource that needs to be disposed or closed but I want only a single catch block around a larger code unit that needs to be aborted if some error occurs inside it.

try {
    // some code
    SomeResource res = new SomeResource();
    try {
        res.use();
    } finally {
        res.close();
    }
    // some more code
} catch( Exception ex ) {
    handleError( ex );
}

This closes the resource as early as possible in either case (error or not) but still handles all possible errors from creating or using the resource in a single catch block.

x4u
+2  A: 

I think you are quite right. From the .Net Framework Design Guidelines, written by some of the top architects at Microsoft:

DO NOT overcatch. Exceptions should often be allowed to propagate up the call stack.

In well-written code, try-finally [or using] is far more common than try-catch. It might seem counterintuitive at first, but catch blocks are not needed in a surprising number of cases. On the other hand, you should always consider whether try-finally [or using] could be of use for cleanup.

page 230 section 7.2

MarkJ
+9  A: 

Not an answer to your question, but a fun fact.

The Microsoft implementation of the C# compiler actually cannot handle try-catch-finally. When we parse the code

try { Foo() } 
catch(Exception e) { Bar(e); }
finally { Blah(); }

we actually pretend that the code was written

try 
{
   try { Foo(); }
   catch(Exception e) { Bar(e); }
}
finally { Blah(); }

That way the rest of the compiler -- the semantic analyzer, the reachability checker, the code generator, and so on -- only ever has to deal with try-catch and try-finally, never try-catch-finally. A bit of a silly early transformation in my opinion, but it works.

Eric Lippert
both pieces are equally the same, aren't they? the only difference is that you could have yet more code right before the 2nd `finally`
Atmocreations
Well if they *weren't* logically the same then it wouldn't be valid for us to make the transformation, so we wouldn't do it. Since they *are* logically the same, we can get away with this sneaky trick.
Eric Lippert
A: 

Another use would be to dispose a file handle to an e-mail attachment when using the System.Web.Mail mail objects to send e-mail. I found this out when I had to programatically open a Crystal Report, save it to disk, attach it to an e-mail, and then delete it from disk. An explicit .Dispose() was required in the Finally to make sure I could delete it, especially in the event of a thrown exception.

HardCode
+1  A: 

How about something like:

  Dim mainException as Exception = Nothing

  Try
    ... Start a transaction
    ... confirm the transaction
  Catch Ex as Exception
    mainException = Ex
    Throw
  Finally
    Try
      ... Cleanup, rolling back the transaction if not confirmed
    Catch Ex as Exception
      Throw New RollbackFailureException(mainException, Ex)
    End Try
  End Try

Assuming here that the RollbackFailureException includes an "OriginalException" field as well as "InnerException", and accepts parameters for both. One doesn't want to hide the fact that an exception occurred during the rollback, but nor does one want to lose the original exception which caused the rollback (and might give some clue as to why the rollback happened).

supercat