C# Exception Handling Explained: Handle Errors Safely

Exception handling in C# provides a structured way to detect and respond to unexpected situations during program execution. Using try, catch, finally, and throw correctly helps applications recover from errors and remain reliable.

What is an Exception?

An exception is an object that represents an error or unusual condition that occurs while a program is running. Examples include invalid input, missing files, unavailable resources, and invalid operations.

CSHARP
int number = int.Parse("abc");

// FormatException occurs because "abc" is not a valid integer.

Why Use Exception Handling?

  • Prevent unexpected application crashes
  • Provide meaningful error messages
  • Recover from expected runtime problems
  • Clean up resources safely
  • Separate normal logic from error-handling logic
  • Create more reliable applications

The try-catch Block

The try block contains code that may throw an exception. The catch block handles an exception when one occurs.

CSHARP
try
{
    int number = int.Parse("abc");
    Console.WriteLine(number);
}
catch (FormatException)
{
    Console.WriteLine("Invalid number format.");
}

Handling Different Exception Types

A program can use multiple catch blocks to handle different exception types separately.

CSHARP
try
{
    int[] numbers = { 10, 20, 30 };
    Console.WriteLine(numbers[10]);
}
catch (IndexOutOfRangeException)
{
    Console.WriteLine("Invalid array index.");
}
catch (Exception ex)
{
    Console.WriteLine($"Unexpected error: {ex.Message}");
}

The finally Block

The finally block runs after the try and catch processing. It is commonly used for cleanup operations that should happen regardless of whether an exception occurred.

CSHARP
try
{
    Console.WriteLine("Processing data...");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
    Console.WriteLine("Cleanup completed.");
}

Using throw

The throw statement allows an application to explicitly raise an exception when an invalid condition is detected.

CSHARP
static void ValidateAge(int age)
{
    if (age < 18)
    {
        throw new ArgumentException("Age must be at least 18.");
    }

    Console.WriteLine("Age is valid.");
}

Rethrowing Exceptions

An exception can be caught, logged or inspected, and then rethrown using throw. Using throw without specifying the exception preserves the original stack trace.

CSHARP
try
{
    ProcessData();
}
catch (Exception)
{
    Console.WriteLine("Logging error...");
    throw;
}

Common Built-In Exceptions

ExceptionTypical CauseExample
ArgumentExceptionInvalid argumentInvalid method parameter
ArgumentNullExceptionNull argumentRequired parameter is null
FormatExceptionInvalid data formatParsing invalid text
InvalidOperationExceptionInvalid operation stateOperation cannot run in current state
FileNotFoundExceptionMissing fileOpening a nonexistent file
IndexOutOfRangeExceptionInvalid indexAccessing outside an array

Custom Exceptions

Custom exceptions can represent application-specific error conditions. They are useful when an error needs a clear domain-specific meaning.

CSHARP
class InsufficientBalanceException : Exception
{
    public InsufficientBalanceException(string message)
        : base(message)
    {
    }
}

static void Withdraw(decimal balance, decimal amount)
{
    if (amount > balance)
    {
        throw new InsufficientBalanceException(
            "Insufficient account balance.");
    }
}

Catching a Custom Exception

CSHARP
try
{
    Withdraw(500, 1000);
}
catch (InsufficientBalanceException ex)
{
    Console.WriteLine(ex.Message);
}

Exception Filters

Exception filters allow a catch block to run only when an additional condition is satisfied.

CSHARP
try
{
    ProcessRequest();
}
catch (Exception ex) when (ex.Message.Contains("network"))
{
    Console.WriteLine("Network-related error.");
}

Exception Handling with File Operations

File operations can fail for several reasons, such as missing files or insufficient permissions. Exceptions can be handled at an appropriate application boundary.

CSHARP
try
{
    string content = File.ReadAllText("data.txt");
    Console.WriteLine(content);
}
catch (FileNotFoundException)
{
    Console.WriteLine("The file does not exist.");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Access to the file was denied.");
}

Exception Handling in Methods

A method does not always need to catch every exception itself. Sometimes it is better to allow an exception to move to a higher-level component that can make the appropriate decision.

CSHARP
static string LoadConfiguration()
{
    return File.ReadAllText("config.json");
}

try
{
    string config = LoadConfiguration();
}
catch (FileNotFoundException)
{
    Console.WriteLine("Configuration file is missing.");
}

Exception Handling vs Validation

Not every invalid input should be handled through exceptions. Expected user input validation is often better handled with normal conditional logic before performing an operation.

CSHARP
string input = "123";

if (int.TryParse(input, out int number))
{
    Console.WriteLine($"Number: {number}");
}
else
{
    Console.WriteLine("Please enter a valid number.");
}

Exception Handling Best Practices

  • Catch only exceptions you can meaningfully handle
  • Use specific exception types instead of catching everything unnecessarily
  • Preserve stack traces when rethrowing with throw
  • Use finally or using for cleanup when appropriate
  • Do not expose sensitive internal error details to users
  • Log unexpected failures at suitable application boundaries
  • Validate expected input without using exceptions as normal control flow

Common Mistakes to Avoid

  • Using empty catch blocks
  • Catching Exception everywhere
  • Ignoring exceptions without logging or handling them
  • Throwing overly generic exceptions
  • Using exceptions for ordinary validation
  • Losing the original stack trace when rethrowing
  • Displaying technical exception details directly to end users

Exception Handling Comparison

TechniquePurposeTypical Usage
try-catchHandle an exceptionRecover or report an error
finallyPerform cleanupRelease resources
throwRaise an exceptionReport invalid conditions
Exception filterConditionally catchHandle specific situations
Custom exceptionRepresent domain-specific errorsBusiness/application rules

Real-World Applications

  • API error handling
  • File processing
  • Database operations
  • Payment processing
  • User input validation
  • Authentication failures
  • External service communication

Practice Exercises

  • Create a try-catch block for invalid number input
  • Handle a missing file exception
  • Create a custom InsufficientBalanceException
  • Use finally for cleanup
  • Practice multiple catch blocks
  • Implement an exception filter
  • Build a small application-level error handler

Conclusion

Effective exception handling helps C# applications deal with unexpected failures in a controlled way. By using specific exceptions, meaningful recovery logic, proper cleanup, and clear validation strategies, developers can create applications that are safer and easier to maintain.

Note: Note: Exceptions should represent exceptional conditions. For expected input validation, prefer normal validation techniques such as TryParse and conditional checks.