20
Lesson 20 of 35 · OOP

Exception Handling

Errors are inevitable. C#'s exception handling mechanism lets you catch and recover from runtime errors gracefully without crashing your program.

try / catch / finally

Wrap risky code in a try block. One or more catch blocks handle specific exception types. The finally block always runs—perfect for cleanup code.

Exception basics Exceptions.cs
try
{
    int[] arr = { 1, 2, 3 };
    Console.WriteLine(arr[10]); // IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
    Console.WriteLine($"Array error: {ex.Message}");
}
catch (Exception ex)  // catch-all
{
    Console.WriteLine($"Unexpected: {ex.Message}");
}
finally
{
    Console.WriteLine("Cleanup runs regardless.");
}

Custom Exceptions

Inherit from Exception (or a more specific class) to create domain-specific exceptions with extra context.

Custom exception CustomEx.cs
public class InsufficientFundsException(decimal amount, decimal balance)
    : Exception($"Cannot withdraw {amount:C2}; balance is {balance:C2}")
{
    public decimal Amount  { get; } = amount;
    public decimal Balance { get; } = balance;
}

// Usage
try
{
    throw new InsufficientFundsException(500m, 100m);
}
catch (InsufficientFundsException ex)
{
    Console.WriteLine(ex.Message);
}

Exception Filters

C# supports when clauses on catch blocks to filter by condition without re-throwing.

Exception filter ExFilter.cs
try
{
    // ... http call
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
    Console.WriteLine("Resource not found (404)");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"HTTP error: {ex.StatusCode}");
}