CodeGym /Courses /C# SELF /Multiple catch and Filters in C#

Multiple catch and Filters in C#

C# SELF
Level 13 , Lesson 4
Available

1. Introduction

Once your program gets a bit more complicated than just Hello, world!, you quickly run into the need to handle different errors in different ways. Imagine: you're working with files, networks, databases — and for each error scenario, you want a different reaction. For example, if a file is missing, you might want to let the user pick another one, but if there's a network error — maybe retry or show a friendly message like "Check the cable, maybe your cat chewed it again."

In C#, you use multiple catch blocks in a row for this. Each block specializes in its own exception type (and its descendants).

Structure of multiple catch


try
{
    // Here — code that might throw different exceptions
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"File not found: {ex.Message}");
}
catch (IOException ex) // catches all IO errors, unless it's FileNotFoundException
{
    Console.WriteLine($"Input/output error: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Something went wrong: {ex.Message}");
}
Multiple catch blocks for different error types

Important! The compiler goes through the blocks from top to bottom and picks the first one that matches the type. If it catches a FileNotFoundException, it won't go further down the chain.

Illustration of the "chain"

Exception type Which block catches it?
FileNotFoundException The first catch
IOException (other) The second catch
ArgumentException The third (general) catch

Why can't you mix them up?
The "widest" catch — like catch (Exception) — should always go last, otherwise it'll "swallow" all exceptions too early, and the specialized catch blocks won't get a chance.

It's like turning off all the fire alarms in a building just because one went off from a burnt toaster: if there's a real fire later, the system won't notice.

Working example

using System;
using System.IO;

class Program
{
    static void Main()
    {
        try
        {
            double result = CalculateAverageAgeFromFile("users.txt");
            Console.WriteLine($"Average age — {result}");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Error: file not found. Check the path.");
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Data error: can't read age.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Other error: {ex.Message}");
        }
    }

    static double CalculateAverageAgeFromFile(string filePath)
    {
        // (Implementation: reads file, parses ages, calculates average)
        // ...
        throw new NotImplementedException();
    }
}

Here we clearly separate what to do if the file is missing (FileNotFoundException), and what to do if the file has some "weird" data (FormatException). All other cases are handled by the "backup" third block.

2. catch Filters: catching subtle stuff

Multiple catch blocks are handy, but sometimes that's not enough. Sometimes, even within one exception type, you want to react differently depending on the circumstances.

For example, if the network isn't working because the internet ran out — maybe we can try to reconnect. But if the server just isn't responding — maybe we should show a different message.

That's where catch filters come in — a real C# superpower that lets you catch not just by type, but also by extra conditions.

Syntax for the when filter


catch (IOException ex) when (ex.Message.Contains("no disk"))
{
    Console.WriteLine("Oops! Looks like you yanked out the flash drive.");
}
catch (IOException ex)
{
    Console.WriteLine("Other input/output error: " + ex.Message);
}
catch when filter for more precise exception handling

Here, the first block only catches those IOException where the message contains the phrase "no disk" — everything else goes to the second block.

Real-life usage

Filters are especially useful for network errors, when you need to decide what to do based on the inner properties of the exception: retry, or just show an error.

Another example: imagine in a method that parses a file, you want not just a generic FormatException, but a special case — if it's about reading the age (like if the age is written as "abc" instead of a number).

catch (FormatException ex) when (ex.Message.Contains("age"))
{
    Console.WriteLine("Error: couldn't read age. Check your data.");
}
catch (FormatException ex)
{
    Console.WriteLine("Data format error: " + ex.Message);
}

Filters and performance

Filters are super handy, but remember that the filter condition is evaluated before entering the catch block, so if it doesn't match — the block body isn't touched at all.

By the way, if the filter itself throws an exception (like if your when expression divides by zero) — that exception will never be caught by this catch block. So be careful with that.

3. Combining multiple catch and filters

Imagine in your project you need to handle not just the exception type, but also its internal state. For example, when working with IOException, you want different behavior if the file system error is about access or about running out of disk space.


try
{
    File.AppendAllText("log.txt", "New entry\n");
}
catch (IOException ex) when (ex.Message.Contains("No space"))
{
    Console.WriteLine("Error: Disk is out of space!");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Error: no permission to write file. Try running as admin.");
}
catch (IOException ex)
{
    Console.WriteLine("Other disk error: " + ex.Message);
}
Combining filters and different catch types

Here we use both a filter and type-based error separation. This kind of flexibility is especially valuable when building a complex app where you want to react informatively to frequent failures.

4. Features and "gotchas" of filters and multiple catch

  • A catch filter can use the exception variable (ex), but can't change it.
  • If you throw an exception inside when, it won't be "caught" by this catch — it'll go up the stack as if the filter wasn't there.
  • The filter logic becomes part of the contract: your teammate needs to know that not every IOException will be caught — only if the condition matches.
  • If you use just one catch for the whole method, but with a filter inside, there's a chance you'll miss some "extra" exceptions. So if in doubt — use explicit, separate blocks.
  • In big apps, you can use filters for centralized logic, like logging only critical errors.

5. Scenarios for interviews and real work

Knowing filters and multiple catch blocks isn't just for style points or a high clean code score. It's a real skill you might be asked about in interviews, especially if the job involves supporting a big, complex, distributed app.

Sample questions:

  • How do you handle different file reading error scenarios in C#, so that for different situations (no file, disk busy, data corrupted) you show different messages?
  • How do you avoid "swallowing" an important error if you put a general catch (Exception) block?
  • What's the point of a catch filter? Can you use throw in it?
2
Task
C# SELF, level 13, lesson 4
Locked
Exception Handling Using Multiple catch Blocks
Exception Handling Using Multiple catch Blocks
2
Task
C# SELF, level 13, lesson 4
Locked
Using catch filters
Using catch filters
1
Survey/quiz
Getting to Know Exceptions, level 13, lesson 4
Unavailable
Getting to Know Exceptions
Call Stack and Creating Your Own Exceptions
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION