# Try-Catch-Finally in C#

## Introduction

C#'s [try-catch-finally](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/exception-handling-statements) statements are fundamental building blocks of the language. While most developers learn exception handling early in their careers and use it frequently in production applications, the mechanics can be surprisingly complex. Exception handling is often discussed during developer job interviews, and even experienced developers can struggle with some of its nuances.

## A Complex Case

Let's examine a challenging code snippet that demonstrates these complexities. What does the following code print to the console?

```csharp
try
{
    try
    {
        throw new Exception2();
    }
    catch (Exception1) when (TestTrue())
    {
        Console.WriteLine("inner ex1");
        throw;
    }
    catch (Exception2) when (TestFalse())
    {
        Console.WriteLine("inner ex2");
        throw;
    }
    finally
    {
        Console.WriteLine("inner finally");
        throw new Exception1();
    }
}
catch (Exception2) when (TestFalse())
{
    Console.WriteLine("outer ex2");
}
catch (Exception1) when (TestTrue())
{
    Console.WriteLine("outer ex1");
}
finally
{
    Console.WriteLine("outer finally");
}


bool TestTrue()
{
    Console.WriteLine("test true");
    return true;
}

bool TestFalse()
{
    Console.WriteLine("test false");
    return false;
}

public class Exception2 : Exception1 { }
public class Exception1 : Exception { }
```

## Understanding the Language Specification

To properly understand the behavior, we need to know three key concepts:

1. How does C# [match](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/exceptions#224-how-exceptions-are-handled) `catch` clauses for a given exception?
2. When and how are `finally` blocks [executed](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/exceptions#224-how-exceptions-are-handled)?
3. How do [exception filters](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/exception-handling-statements#exception-filters-vs-traditional-exception-handling) (using the `when` keyword) work?

According to the .NET 10 and C# language specification ([v8-draft](https://github.com/dotnet/csharpstandard/tree/draft-v8/)):

> "The search continues until a catch clause is found that can handle the current exception, by naming an exception class that is of the same class, or a base class, of the run-time type of the exception being thrown."

> "Exception filters (when): The filter expression is evaluated before the stack is unwound. This means the original call stack and all local variables remain intact during filter evaluation."

> "Once a matching catch clause is found, the system prepares to transfer control to the first statement of the catch clause. Before execution of the catch clause begins, the system first executes, in order, any finally clauses that were associated with try statements more nested than the one that caught the exception."

## Understanding the Code Snippet Output

The code prints the following output on the console:

```
test true
inner ex1
test false
test true
inner finally
test true
outer ex1
outer finally
```

Let's review the execution step-by-step with the language reference in mind:

1. An exception typed as `Exception2` is thrown: `throw new Exception2();`.
1. `Exception1` is the base type of the thrown exception, hence the first inner `catch` clause matches it.
1. The `when` clause is executed for the first inner `catch` clause, it prints `test true`.
1. The exception filter also returns `true`, so the first inner `catch` block is executed, printing `inner ex1`.
1. This `catch` block re-throws the exception while preserving the original stack trace.
1. An enclosing `catch` clause is being searched for.
1. The first outer `catch` block matches the type of the exception, it could handle the exception.
1. The `when` clause of the first outer `catch` clause is executed, which prints `test false` to the console and returns `false`.
1. The second outer `catch` clause matches the type of the exception, it could handle the exception.
1. The `when` clause of the second outer `catch` clause is executed, which prints `test true` to the console and returns `true`.
1. Before the execution of the second outer `catch` block begins, the inner `finally` block is executed, printing `inner finally` on the console.
1. The inner `finally` block throws a new exception, typed `Exception1`.
1. The second outer `catch` clause matches the type of this new exception.
1. The `when` clause of the second outer `catch` clause is executed, which prints `test true` to the console and returns `true`.
1. The second outer `catch` block is executed, printing `outer ex1` on the console.
1. The exception is handled by the block.
1. The outer finally block is executed, printing `outer finally` on the console.

This example illustrates the complexity of exception handling, particularly when dealing with nested try-catch blocks, exception filters, and finally blocks that throw new exceptions.