C# Exception Handling Best Practices

Explore practical examples of C# exception handling best practices to enhance your coding skills.
By Jamie

Introduction to Exception Handling in C

Exception handling is a critical aspect of programming in C#, allowing developers to anticipate, manage, and respond to runtime errors effectively. By implementing best practices in exception handling, you can improve code reliability and maintainability. Below are three practical examples that showcase these best practices in C#.

Example 1: Specific Exception Catching

Context

In this scenario, you are working on a method that reads a file and processes its content. It’s crucial to handle specific exceptions that may arise during file operations rather than catching all exceptions indiscriminately.

using System;
using System.IO;

class Program
{
    static void Main()
    {
        try
        {
            string content = ReadFile("data.txt");
            Console.WriteLine(content);
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Error: File not found. Please check the file path.");
            // Log exception details for further investigation
            LogError(ex);
        }
        catch (IOException ex)
        {
            Console.WriteLine("Error: An I/O error occurred. Please try again later.");
            LogError(ex);
        }
    }

    static string ReadFile(string filePath)
    {
        return File.ReadAllText(filePath);
    }

    static void LogError(Exception ex)
    {
        // Log details of the exception here
        Console.WriteLine(ex.Message);
    }
}

Notes

  • Catching specific exceptions helps in accurately determining the cause of the failure.
  • Always log exception details to assist with debugging.

Example 2: Using Finally Block for Resource Cleanup

Context

When working with unmanaged resources such as database connections, it’s essential to ensure that resources are properly released, even if an exception occurs. This example demonstrates how to use a finally block for cleanup.

using System;
using System.Data.SqlClient;

class Program
{
    static void Main()
    {
        SqlConnection connection = null;
        try
        {
            connection = new SqlConnection("YourConnectionStringHere");
            connection.Open();
            // Execute database operations
        }
        catch (SqlException ex)
        {
            Console.WriteLine("SQL Error: " + ex.Message);
            // Handle SQL exceptions
        }
        finally
        {
            // Ensure the connection is closed
            if (connection != null)
            {
                connection.Close();
                Console.WriteLine("Connection closed.");
            }
        }
    }
}

Notes

  • The finally block is executed regardless of whether an exception is thrown or not, making it ideal for resource cleanup.
  • This pattern helps prevent resource leaks, which can lead to performance issues.

Example 3: Custom Exception Classes

Context

In some cases, you might want to create your own exceptions to represent specific error conditions in your application. This example illustrates how to define and use a custom exception class.

using System;

// Define a custom exception class
public class InvalidAgeException : Exception
{
    public InvalidAgeException(string message) : base(message) {}
}

class Program
{
    static void Main()
    {
        try
        {
            SetAge(-1);
        }
        catch (InvalidAgeException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static void SetAge(int age)
    {
        if (age < 0)
        {
            throw new InvalidAgeException("Age cannot be negative.");
        }
        // Set age logic here
    }
}

Notes

  • Custom exceptions enable more meaningful error handling tailored to your application.
  • They improve code readability and maintenance, as developers can easily understand the type of error being handled.