Interesting Exception Results: Throwing Exceptions From the Finally Block

Note: I'm using .NET 3.5/VS 2008.

This code:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Program.CallMe();
            }
            catch(Exception e)
            {
                Console.Out.WriteLine(e.GetType().FullName);
            }
        }

        private static void CallMe()
        {
            try
            {
                throw new NotSupportedException();
            }
            finally
            {
                throw new NotImplementedException();
            }
        }
    }
}

produces this:

System.NotImplementedException

Press any key to continue . . .

But this code:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Program.CallMe();
        }

        private static void CallMe()
        {
            try
            {
                throw new NotSupportedException();
            }
            finally
            {
                throw new NotImplementedException();
            }
        }
    }
}

produces this:

Unhandled Exception: System.NotSupportedException: Specified method is not supported.
   at ConsoleApplication1.Program.CallMe() in C:\Documents and Settings\jasonb\My Documents\Visual Studio 2008\Projects\ConsoleApplication1\Program.cs:line 16
   at ConsoleApplication1.Program.Main(String[] args) in C:\Documents and Settings\jasonb\My Documents\Visual Studio 2008\Projects\ConsoleApplication1\Program.cs:line 9

Unhandled Exception: System.NotImplementedException: The method or operation is not implemented.
   at ConsoleApplication1.Program.CallMe() in C:\Documents and Settings\jasonb\My Documents\Visual Studio 2008\Projects\ConsoleApplication1\Program.cs:line 20
   at ConsoleApplication1.Program.Main(String[] args) in C:\Documents and Settings\jasonb\My Documents\Visual Studio 2008\Projects\ConsoleApplication1\Program.cs:line 9

Press any key to continue . . .

That was very...odd (same behavior in VB). Just why is the console window showing 2 exceptions? I'm not saying that the caller is getting 2 exceptions, but...that output is just strange. In a WinForms version of this:

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Application.ThreadException += (sender, e) =>
                this.textBox1.Text += e.Exception.GetType().FullName;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.CallMe();
        }

        private void CallMe()
        {
            try
            {
                throw new NotSupportedException();
            }
            finally
            {
                throw new NotImplementedException();
            }
        }
    }
}

I only see one exception in the text box. So...why does the console show 2? I'm sure there's an easy explanation but I have to go to bed.

* Posted at 04.18.2008 01:44:28 AM (Last Update: 04.18.2008 01:59:48 PM) | 3 comments | Link | RSS *

Comments

# C# spec, from Joshua McKinney at 04.18.2008 03:48:37 AM

1. In your in your first example, the clr travels up the stack to find the catch block. Because the catch is found, the finally block runs. The finally block throws, terminating the execution of the catch block, and causing the clr to look for a catch block again. So your catch is never exectuted for the first exception due to the second exception.
2. I'm not really sure, but I assume this is the clr doing some magic for unhandled exceptions.
3. Similar to a combination of 1. and 2.

Generally it's a pretty bad idea to throw from your finally blocks due to the lack of ability to handle the exception from outer catches, but I guess if you're throwing in the finally, you're probably mostly screwed anyway.
See below from the c# spec section 8.9.5:
The throw statement throws an exception.
throw-statement:
throw expressionopt ;
A throw statement with an expression throws the value produced by evaluating the expression. The expression must denote a value of the class type System.Exception, of a class type that derives from System.Exception or of a type parameter type that has System.Exception (or a subclass thereof) as its effective base class. If evaluation of the expression produces null, a System.NullReferenceException is thrown instead.
A throw statement with no expression can be used only in a catch block, in which case that statement re-throws the exception that is currently being handled by that catch block.
Because a throw statement unconditionally transfers control elsewhere, the end point of a throw statement is never reachable.
When an exception is thrown, control is transferred to the first catch clause in an enclosing try statement that can handle the exception. The process that takes place from the point of the exception being thrown to the point of transferring control to a suitable exception handler is known as exception propagation. Propagation of an exception consists of repeatedly evaluating the following steps until a catch clause that matches the exception is found. In this description, the throw point is initially the location at which the exception is thrown.
• 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:
o 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.
o 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.
• If an exception handler was not located in the current function member invocation, the function member invocation is terminated. The steps above are then repeated for the caller of the function member with a throw point corresponding to the statement from which the function member was invoked.
• If the exception processing terminates all function member invocations in the current thread, indicating that the thread has no handler for the exception, then the thread is itself terminated. The impact of such termination is implementation-defined.

# Thanks, from Jason Bock at 04.18.2008 10:04:19 AM

Joshua,

If I would've have time to stay up, I would've hit the spec :). The fact that the caller was seeing the exception from the finally makes sense, but the console app behavior was bizarre.

I totally agree, explicitly throwing an exception from a finally block is a terrible idea. But...for my ExceptionFinder add-in, I want to cover all possible situations, and this is one of them.

# Inner Exception, from Justin Chase at 04.18.2008 01:59:48 PM

Is the first exception getting put into the inner exception of the second exception? Perhaps try calling ToString() on the caught exception rather than GetType().FullName.

Add a Comment

(*) = Required field
Name (*):

E-Mail (*):

Web Site:

Title (*):

Comments (*):

Enter the code you see (*)



Quote
"The whole world seems to live under the banner: 'Freedom is wonderful--but only for me.'" Isaac Asimov
Twitter History
follow me on Twitter
Blog History