Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The try-catch statement in C# is a structured exception handling mechanism that allows developers to intercept and process runtime errors (exceptions). It prevents unhandled exceptions from propagating up the call stack and abruptly terminating the application process. When an exception is thrown within a guarded region of code, the Common Language Runtime (CLR) employs a two-pass exception handling model. In the first pass, the CLR searches the call stack to locate a matching catch handler and evaluates any when exception filters without unwinding the stack. If a compatible handler is found, the second pass initiates stack unwinding, executing any finally blocks along the way, before transferring control to the matched catch block.

Syntax

using System;
using System.IO;

public class ExceptionHandlingExample
{
    public void Execute()
    {
        try
        {
            // Guarded region: Code that may throw an exception
            string content = File.ReadAllText("data.txt");
        }
        catch (FileNotFoundException ex) when (ex.FileName != null)
        {
            // Handler for FileNotFoundException that meets the filter condition
            Console.WriteLine($"File not found: {ex.FileName}");
        }
        catch (IOException ex)
        {
            // Handler for IOException
            Console.WriteLine($"I/O error: {ex.Message}");
        }
        catch (Exception ex)
        {
            // Fallback handler for any exception derived from System.Exception
            Console.WriteLine($"General error: {ex.Message}");
        }
        finally
        {
            // Execution region: Runs regardless of exception occurrence
            Console.WriteLine("Execution completed.");
        }
    }
}

Core Components

1. The try Block

The try block defines the scope of the guarded code. If an instruction within this block throws an exception, the CLR immediately halts execution of subsequent instructions in the block and begins the first pass of the exception handling model to find a matching catch block.

2. The catch Block

The catch block acts as the exception handler. It can specify an exception type (which must derive from System.Exception), or it can be declared as a general catch { } block without any type declaration or parentheses to catch all exceptions.
  • Type Matching: The CLR evaluates catch blocks sequentially. It executes the first block where the thrown exception’s type matches, or is derived from, the declared exception type.
  • Ordering: Because of sequential evaluation, catch blocks must be ordered from the most specific (most derived) exception type to the least specific (base) type. A compilation error occurs if a base class catch block precedes a derived class catch block. Furthermore, a general catch { } block cannot be combined with a catch (Exception) block on the same try statement, as catch (Exception) already catches all exceptions, rendering the general block unreachable (Compiler Error CS0160).
  • The Exception Variable: Declaring a variable (e.g., ex) captures the exception object, granting access to its properties, such as Message, StackTrace, and InnerException. The variable can be omitted if the exception object itself is not needed (catch (IOException)).

3. Exception Filters (when Keyword)

Introduced in C# 6.0, the when keyword allows a catch block to specify a boolean condition that must evaluate to true for the block to handle the exception.
  • Stack Preservation: Exception filters are evaluated during the first pass of the CLR’s exception handling model. Because the stack is not unwound during this pass, if the filter evaluates to false, the CLR continues searching for a handler while preserving the original stack context and memory state for debugging.

4. The finally Block

The finally block contains code that executes after the try block completes successfully, or after a catch block finishes handling an exception. It executes even if a catch block throws a new exception or contains a control transfer statement (like return or break).
  • Execution Exceptions: The finally block is not absolutely guaranteed to execute. In modern .NET (.NET Core and .NET 5+), if an exception is completely unhandled across the entire call stack, finally blocks do not execute. The runtime terminates the process immediately to preserve the memory state for crash dumps. Additionally, finally blocks are bypassed if the process is terminated abruptly via Environment.FailFast() or Environment.Exit(), or due to catastrophic runtime failures such as a StackOverflowException.

Exception Propagation and Re-throwing

If no matching catch block is found within the current method, the CLR propagates the exception up the call stack to the invoking method. This continues until a compatible handler is found or the top of the stack is reached, resulting in process termination. When handling an exception, developers can re-throw it to a higher-level handler using the throw keyword.
using System;

public class RethrowExample
{
    public void ProcessDataCorrectly()
    {
        try
        {
            PerformOperation();
        }
        catch (InvalidOperationException)
        {
            // Alternative 1: Preserving the stack trace (Correct)
            // Logs or processes the exception, then re-throws
            throw; 
        }
    }

    public void ProcessDataIncorrectly()
    {
        try
        {
            PerformOperation();
        }
        catch (InvalidOperationException ex)
        {
            // Alternative 2: Overwriting the stack trace (Generally Incorrect)
            // Treats the exception as a newly thrown object
            throw ex; 
        }
    }

    private void PerformOperation()
    {
        throw new InvalidOperationException("Operation failed.");
    }
}
These approaches are mutually exclusive. Using throw; (re-throw) maintains the original exception’s stack trace. Using throw ex; treats the exception as a newly thrown object, overwriting the stack trace and destroying the historical context of the error. Placing both sequentially in the same block will result in a compiler warning (CS0162: Unreachable code detected) because the first throw statement unconditionally transfers control out of the block.
Master C# with Deep Grasping Methodology!Learn More