views:

136

answers:

8
+4  Q: 

throw-catch logic

try
{
    try
    {
        throw new Exception("From Try");
    }
    catch
    {
        throw new Exception("From Catch");
    }
    finally
    {
        throw new Exception("From Finally");
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

The above code's output is: From Finally.

Why it's not From Catch?

-or-

How can i catch & log from outside both exceptions?

A: 

finally always runs; and it always runs last. So the lat thing done by the inner try was the finally and that threw something that was caught by the outer catch

not sure if i understand part2 of the question

pm100
Note that the finally block does *not* always run. There are many situations in which the finally block does not run. (For example, if the try contains a "fail fast".)
Eric Lippert
@eric what does 'fail fast' mean
pm100
It means that you fail fast. Instead of failing slowly. That is, when something goes wrong you stop everything *immediately* so that you don't make anything *worse*. Failing slowly, where you log the failures and try to close files and save user data and blah blah blah might be a good idea. On the other hand, halting the robot arm before it knocks over the ten thousand litre vat of pudding might be a better idea than spending a few milliseconds trying to recover. If you put a fail fast in a finally block, no other finally blocks run. The process simply fails immediately.
Eric Lippert
A: 

finally happens no matter what. Regardless of whether there was an exception in the try or catch. Thus, you see "From Finally". (This actually is the entire purpose of the finally clause. So you can put code in there that will clean up resources and the like no matter what -- even if there's an exception.)

Kirk Woll
+6  A: 

Because the finally block executes after the catch block, overriding the exception.

And when an exception happens during the handling of an earlier one, the first one is lost.

How can i catch & log from outside both exceptions?

  1. By not throwing inside a finally block. That is always a bad idea.
  2. If you want to log in an inner catch block use throw; or pass the first exception as InnerException of the new one. That is why InnerException exists.
Henk Holterman
Of course I'm not throwing exceptions by myself in the finally block, exceptions can be thrown while releasing resources in finally block, its a normal behavior and i want to be informed about an exception there, and it's not conflicting with that i want the "From Catch" exception too.
DxCK
@dxck, it's not that normal. Resource releasing code should not normally throw. Do you have a concrete example?
Henk Holterman
For example: Writing to a temp file failed because disk ran out of space, then in the finally block i want to delete the file, but it fails because an IO error or someone messed with the file (for example disk cleaning program).
DxCK
After the temp file opened successfully you can and should write the finally part so that it doesn't throw. The close won't throw and under these conditions neither will Delete.
Henk Holterman
DxCK
Right, but notice that Close() does _not_ throw any exceptions. No coincidence. Using Delete() in a finally is a-typical, it appears safe for local files. For a network file you would use 2 Deletes, one in the happy path and one in a catch block.
Henk Holterman
A: 

Your code throws a new Exception from each part of the try/catch/finally statement. You are essentially swallowing the previous exception when you create the new error. You can add your "From Try" message to your "From Catch" message with something like

catch(Exception ex)
{
    throw new Exception(ex.Message + ":" + "From Catch");
}

I don't know know how you could chain that in the finally though.

Beaner
A: 

Because the finally block is always executed.

try 
{ 
    try 
    { 
        throw new Exception("From Try"); // (1) A new exception object A is created here and thrown.
    } 
    catch // (2) Exception object A is catched.
    { 
        throw new Exception("From Catch"); // (3) A new exception object B is created here and thrown.
    } 
    finally // (4) Execution is forced to continue here!
    { 
        throw new Exception("From Finally"); // (5) A new exception object C is created here and thrown.
    } 
} 
catch (Exception ex) // (6) Exception object C is catched.
{ 
    Console.WriteLine(ex.Message); 
} 

Every new'd exception object in step (3) and (5) discards the previous one. Since the finally block is always executed all what remains is the exception object C from step (5).

Theo Lenndorff
+1  A: 

This is the behaviour as it is defined by the C# language specification. Handling of the exception thrown inside the try block is aborted and instead the exception thrown in the finally block will be handled.

The relevant section 8.9.5 The throw statement explains how exceptions are propagates:

  • In the current function member, each try statement that encloses the throw point is examined. For each statement S, starting with the innermost try statement and ending with the outermost try statement, the following steps are evaluated:

    • If the try block of S encloses the throw point and if S has one or more catch clauses, the catch clauses are examined in order of appearance to locate a suitable handler for the exception. The first catch clause that specifies the exception type or a base type of the exception type is considered a match. A general catch clause (§8.10) is considered a match for any exception type. If a matching catch clause is located, the exception propagation is completed by transferring control to the block of that catch clause.

    • Otherwise, if the try block or a catch block of S encloses the throw point and if S has a finally block, control is transferred to the finally block. If the finally block throws another exception, processing of the current exception is terminated. Otherwise, when control reaches the end point of the finally block, processing of the current exception is continued.

0xA3
A: 

This is a very good question, and one that is kind of tricky. Let's go through this step by step:

try
{
    throw new Exception("From Try");
}
catch
{
    throw new Exception("From Catch");
}

In the code above, Exception("From Try") is thrown and caught by the catch clause (pretty simple so far). The catch clause throws an exception of it's own, which normally we would expect (because the catch is nested in a larger try-catch block) to be caught immediately, but...

finally
{
   throw new Exception("From Finally");
}

The finally clause, which is guaranteed to (try to) execute, comes first, and throws an exception of it's own, overwriting the Exception("From Catch") that was thrown earlier.

"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" - MSDN Article

Following this train of logic, we should try our best to refrain from writing code in our catch and finally blocks that is exception-prone. If you're worried about situations like the one you presented cropping up, I'd recommend logging the exceptions and their related information out to an external file, which you can reference for debugging.

Ari Patrick
+1  A: 

Add an extra layer of try-catch blocks like the following:

try {
    Exception fromCatch = null;
    try {
        throw new Exception("From Try");
    }
    catch {
        try {
            throw new Exception("From Catch");
        }
        catch (Exception e) {
            // catch failed -> store exception
            fromCatch = e;
        }
    }
    finally {
        try {
            throw new Exception("From Finally");
        }
        catch (Exception e) {
            // i can think of better exception merging... but this shows the idea
            throw new Exception(e.Message, fromCatch);
        }
        // throw fromCatch, in case "From Finally did not happen"
        throw fromCatch;
    }
}
catch (Exception ex) {
    Console.WriteLine(ex.Message);
    if (ex.InnerException != null) {
        Console.WriteLine(ex.InnerException.Message);
    }
}

Reports:

From Finally
From Catch

Edit: this is obviously the answer for question two, as the "why" is answered sufficiently :)

Jens Nolte