3

Here's a pseudo-code:

try
{
  TextReader tr = new StreamReader("filename");
  try
  {
    string content = tr.ReadToEnd();
  }
  catch(Exception ex)
  { /*Show error message*/ }
  finally
  { tr.Close();}
}
catch(Exception ex)
{ /*Show error message*/ }

Ok, that code is a sample of a book. What i don't understand is why a nested try-catch block is used.

If

string content = tr.ReadToEnd();

goes wrong then outer catch should catch the exception right? So I can't see any point of using extra try-catch for that line!

If you see any misconceptions here then I'd be glad if you could point them out!

Also are there any cases where nested try-catch would be reasonable to use?

yoozer8
  • 7,361
  • 7
  • 58
  • 93
Peacelyk
  • 1,126
  • 4
  • 24
  • 48
  • To detect when the error occured, On `new StreamReader` or on `ReadToEnd`? – khachik Apr 15 '11 at 20:42
  • possible duplicate of [Is it best practice to try - catch my entire PHP code, or be as specific as possible?](http://stackoverflow.com/questions/2831263/is-it-best-practice-to-try-catch-my-entire-php-code-or-be-as-specific-as-possi) –  Apr 15 '11 at 20:44
  • @0A0D - How can this be a dupe of a question about PHP? – Jeffrey L Whitledge Apr 15 '11 at 21:28
  • @Jeffrey: This is a language-agnostic question regardless of language. –  Apr 15 '11 at 22:07
  • 3
    @0A0D - No, it isn't. Different languages approach exception handling in different ways. This was a C# question until you removed the tag. – Jeffrey L Whitledge Apr 16 '11 at 00:10
  • @Jeffrey: PHP handles Exception handling very similar to C#. In fact, C# handles it similar to C++. So it is language-agnostic. –  Apr 16 '11 at 01:47
  • @0A0D: PHP doesn't have a `finally` construct. – BoltClock Apr 16 '11 at 11:30
  • @BoltClock: There are some minor syntactical differences but this question is asking whether it is wise to let the exception to be caught locally or globally, which is an agnostic question no matter how you slice it. –  Apr 16 '11 at 12:23
  • @0A0D: Categorically no. The fact that cars and trucks both have wheels does not make "what kind of tires should I get" a "vehicle-agnostic" question. Kindly look into exception handling in a few (dozen) more languages before making these sorts of claims. – Derrick Turk May 12 '11 at 14:29
  • @Derrick: It would be if you asked what is the purpose of tires on vehicles - which if given in the context of this question would make it agnostic. –  May 25 '11 at 00:07

4 Answers4

3

Differences between your example and similar code in which there is only one, outer, catch:

1) If the inner block throws, then the message is printed before calling Close(). This might be significant if Close() also contains logging.

2) If an inner exception is thrown and caught, and then in the finally block Close() also throws, you get two exceptions and you handle two exceptions. With only one catch block, if Close() throws then what happens next might depend on language, but you won't get two error messages printed. I'd expect the second exception to replace the first one, but I don't swear that's what happens in all languages with finally, or even all languages I've used...

3) //Show error message is just a comment, it does nothing. It may be the intention of the author that you'd show a different error message in the two different cases. The inner one would say, "read failed", and the outer one would say, "cannot open file". Or something like that. To achieve that with only one catch block, you could set and check a flag indicating progress (which probably doesn't make the code any simpler than two catch blocks), or you could rely on the exception itself to contain the appropriate error message (in which case good luck with integrating your localization framework with exception-throwing functions in built-in or third-party libraries).

Note that even if you only have one catch, you still need two tries because of the finally. This is no good:

try {
    TextReader tr = new StreamReader("filename");
    string content = tr.ReadToEnd();
} catch(Exception ex) {
    // show error message
} finally {
    tr.Close();
}

Because by the looks of this language, tr will not be in scope in the finally block, so we can't close it there. We have to close it inside the block that deals with creating it. It may be that we could deal with it like this:

TextReader tr = null;
try {
    tr = new StreamReader("filename");
    string content = tr.ReadToEnd();
} catch(Exception ex) {
    // show error message
} finally {
    if (tr != null) {
        tr.Close();
    }
}

But that doesn't really simplify things much compared with the original code, we still have differences 2-3 to contend with, and now we don't handle exceptions from Close() at all. So for many purposes the original code with multiple try/catch was better.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • I think the important point is #2. the outer try catch catches exceptions on new StreamReader, eg FileNotFound, etc and it also encapsulates the exceptions on tr.close(). This leaves the inner try catch for handling read errors, not "connection" type errors. – FoneyOp Apr 15 '11 at 23:07
  • @FoneyOp: maybe, although it's hard to tell with example code what it's supposed to be illustrating which is why I've mentioned everything and the kitchen sink. I wouldn't normally worry too much about `Close()` throwing when I'm *reading* a stream, so I'm not sure whether it's all that significant here. If I was writing, errors on `Close()` become very interesting indeed, so maybe this code is supposed to be setting us up well for stream-handling in general. – Steve Jessop Apr 15 '11 at 23:14
  • Agreed. Or maybe database connections, etc. – FoneyOp Apr 16 '11 at 03:02
2

if string content = tr.ReadToEnd(); goes wrong then outer catch should catch the exception right?

No, the inner catch will catch it and display the error message. Then the inner finally is executed. Then the outer try block finishes without any exceptions to handle, because the inner try-catch block has already dealt with it.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
2

One example I use for is when I need to handle a specific type of exception:

    try
    {

           //...
           //IMPORTANT CODE HERE
           //....
        try
        {
            // Read in non-existent file.
            using (StreamReader reader = new StreamReader("not-there.txt"))
            {
            reader.ReadToEnd();
            }
        }
        catch (FileNotFoundException ex)
        {
           //Do something important here
        }
       //...
       //MORE IMPORTANT CODE
       //...
    }
    catch(Exception ex) ///An Exception I'm not expecting
    { //oops...handle gracefully here
 }
ray
  • 8,521
  • 7
  • 44
  • 58
1

You want to catch the exception as early as possible. It is bad practice to let things bubble to the outer exception. Remember the rule: Fail early, Fail often.

  • 3
    I don't agree with this as a generality. *If* you can usefully do different things in the different cases, then you should. If you would do the same thing either way, then it is bad practice *not* to let things bubble up to the point in your code most able to handle the error. "Fail early" means *throw* the exception ASAP, but you should never catch an exception until you can do something useful with it. – Steve Jessop Apr 15 '11 at 23:09