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?

Answers


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.

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.


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 :)


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


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.)


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.


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.


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).


Need Your Help