[C#] Catching Multiple Exception Types and Implementing Specific Error Handling

In processes like file operations or network communication, a single operation can have multiple different error causes. For example, when reading a file, the guidance for the user and the system’s recovery response differ significantly between “File not found” and “No access permission.”

In C#, by writing multiple catch blocks in the try-catch syntax, you can implement fine-grained error handling based on the type of exception that occurred. In this article, I will explain how to appropriately properly categorize and handle exceptions using a configuration file reading process as an example.

目次

Distributing Exceptions with Multiple Catch Blocks

An important principle in exception handling is “Write specific (detailed) exceptions first, and general exceptions last.”

C# exception classes have an inheritance relationship. If you catch a parent class (e.g., Exception), it includes all of its child class exceptions. Therefore, if you write the parent class first, the program will never reach the specific exception handling (child class catch blocks) written afterwards (this will result in a compilation error).

Practical Code Example: Reading a Configuration File

The following code processes reading an application configuration file. It distinguishes between four cases: “File not found,” “Directory not found,” “No permission,” and “Other IO errors.”

using System;
using System.IO;

namespace ConfigurationManager
{
    class Program
    {
        static void Main()
        {
            // Scenario:
            // Read an important configuration file (settings.xml) at application startup.
            // We need to change the message to the user depending on the error that occurs.

            string filePath = @"Configs\settings.xml";

            try
            {
                // Attempt to read file
                // Various IO errors can occur here.
                string content = File.ReadAllText(filePath);
                
                Console.WriteLine("Successfully read the configuration file.");
                Console.WriteLine($"Content: {content}");
            }
            // 1. Most specific exception: The file itself does not exist
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("[Error] Configuration file not found.");
                Console.WriteLine($"Filename: {ex.FileName}");
                // Recovery idea: Write code to generate default settings here
            }
            // 2. Specific exception: The directory in the path does not exist
            catch (DirectoryNotFoundException)
            {
                Console.WriteLine("[Error] Configuration folder (Configs) does not exist.");
                // Recovery idea: Write code to create the folder here
            }
            // 3. Specific exception: No permission to access the file
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("[Error] No permission to access the file. Please run as administrator.");
            }
            // 4. Slightly general exception: Other IO errors (Disk full, file in use, etc.)
            catch (IOException ex)
            {
                Console.WriteLine($"[Error] An IO error occurred while reading the file: {ex.Message}");
            }
            // 5. Most general exception: All unexpected errors
            catch (Exception ex)
            {
                Console.WriteLine($"[Critical Error] An unexpected problem occurred: {ex.Message}");
            }
        }
    }
}

Example Execution Result (When file is missing)

[Error] Configuration file not found.
Filename: settings.xml

Technical Points and Precautions

1. Inheritance Relationship and Order of Description

Some of the exception classes appearing in the code above have the following inheritance relationship:

  • Object
    • Exception (Base of all exceptions)
      • SystemException
        • IOException
          • FileNotFoundException
          • DirectoryNotFoundException

If you write catch (IOException) above catch (FileNotFoundException), “File not found” errors will also be caught by the IOException block, preventing individual processing. While the compiler may issue a warning or error about unreachable code, you should remember the basic rule: “Derived classes (children) go on top, Base classes (parents) go on the bottom.”

2. Using Exception Filters (when clause)

In C# 6.0 and later, you can use the when keyword to filter whether to catch an exception based on specific conditions (such as property values), even for the same exception type.

catch (IOException ex) when (ex.Message.Contains("Disk full"))
{
    Console.WriteLine("Disk space is insufficient.");
}

This enables fine-grained control that cannot be handled by type branching alone.

Summary

Properly catching and classifying multiple exceptions is essential for creating robust applications. If you simply process all errors together with Exception, users will not understand why the operation failed, and appropriate action cannot be taken. Use this pattern to clarify the cause of errors and provide recovery processing tailored to the situation.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次