SyntaxStudy
Sign Up
C# Try, Catch, and Finally
C# Beginner 1 min read

Try, Catch, and Finally

C# uses structured exception handling with `try`, `catch`, and `finally` blocks. Code that might throw an exception goes inside `try`. One or more `catch` blocks handle specific exception types, ordered from most-specific to least-specific. The `finally` block always runs regardless of whether an exception occurred, making it the right place for cleanup code. Exception filters (the `when` clause on a `catch` block) let you catch an exception only when an additional condition is true. This allows more precise handling without catching and re-throwing. The filter expression is evaluated before the stack is unwound, which preserves the original stack trace for debugging. Re-throwing with `throw` (no argument) inside a `catch` block preserves the original stack trace. Using `throw ex` (with argument) replaces the stack trace with the current location. Always prefer `throw` alone when you want to log and re-throw without losing diagnostic information.
Example
// try/catch/finally, exception filters, and re-throw

static int Divide(int numerator, int denominator)
{
    if (denominator == 0)
        throw new DivideByZeroException("Denominator must not be zero.");
    return numerator / denominator;
}

// Basic try/catch/finally
try
{
    int result = Divide(10, 0);
    Console.WriteLine(result);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Math error: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Unexpected: {ex}");
}
finally
{
    Console.WriteLine("Cleanup in finally block.");
}

// Exception filter with 'when'
static string ReadConfig(string path)
{
    try
    {
        return File.ReadAllText(path);
    }
    catch (IOException ex) when (ex.HResult == unchecked((int)0x80070002))
    {
        return ""; // file not found — return empty config
    }
    catch (IOException ex)
    {
        throw; // other IO errors — preserve original stack trace
    }
}

// Catching multiple exception types in one clause (C# 6)
static void ParseInput(string input)
{
    try
    {
        int n = int.Parse(input);
        Console.WriteLine(1000 / n);
    }
    catch (Exception ex) when (ex is FormatException or DivideByZeroException)
    {
        Console.WriteLine($"Invalid input '{input}': {ex.Message}");
    }
}

ParseInput("abc");
ParseInput("0");
ParseInput("5");