Exception Handling in C#

Exception Handling in C#

Exception Handling in C#: A Complete Guide

1. Introduction to Exception Handling

In C#, exception handling is a powerful mechanism that allows developers to gracefully handle runtime errors, ensuring the program doesn’t crash unexpectedly. Exceptions are unexpected events that occur during program execution, such as trying to divide by zero, file not found, or accessing null references.

1.1 What Is an Exception?

An exception is an error that occurs at runtime and disrupts the normal flow of the program. C# exceptions are represented by the System.Exception class and its many derived types like NullReferenceException, DivideByZeroException, IndexOutOfRangeException, etc.

1.2 Why Handle Exceptions?

Without exception handling, your application may terminate suddenly when an error occurs. Handling exceptions ensures your application remains robust, user-friendly, and reliable. You can log errors, show friendly messages to users, and take corrective actions programmatically.

2. Basic Try-Catch Block

The try-catch statement is the most common form of exception handling in C#. Code that might throw an exception is wrapped inside the try block. The catch block handles the exception and prevents the program from crashing.

2.1 Syntax of Try-Catch

try
{
    // Code that may throw an exception
}
catch (Exception ex)
{
    // Handle exception
    Console.WriteLine("Error occurred: " + ex.Message);
}

2.2 Catching Specific Exceptions

You can have multiple catch blocks to handle different types of exceptions separately.

try
{
    int result = 10 / int.Parse("0");
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("Cannot divide by zero.");
}
catch (FormatException ex)
{
    Console.WriteLine("Invalid input format.");
}
catch (Exception ex)
{
    Console.WriteLine("An unexpected error occurred.");
}

2.3 Nested Try-Catch

It’s possible to nest try-catch blocks for finer control, especially in complex applications.

try
{
    Console.WriteLine("Outer try block");
    try
    {
        Console.WriteLine("Inner try block");
        int x = int.Parse("abc");
    }
    catch (FormatException)
    {
        Console.WriteLine("Handled inner exception.");
    }
}
catch (Exception)
{
    Console.WriteLine("Handled outer exception.");
}

3. The Finally Block

The finally block is used to execute code regardless of whether an exception was thrown or not. It's often used to release resources like file handles, database connections, or cleanup tasks.

try
{
    Console.WriteLine("Trying something risky...");
    int[] nums = { 1, 2, 3 };
    Console.WriteLine(nums[5]);
}
catch (IndexOutOfRangeException)
{
    Console.WriteLine("Index out of range!");
}
finally
{
    Console.WriteLine("This will always execute.");
}

4. Throwing Exceptions

In C#, you can manually throw an exception using the throw keyword. This is useful when you want to enforce rules or constraints in your application. You can throw predefined exceptions or your own custom exceptions.

int age = -5;
if (age < 0)
{
    throw new ArgumentException("Age cannot be negative.");
}

Once thrown, the exception must be caught by a surrounding try-catch block, or it will crash the application.

try
{
    int age = -1;
    if (age < 0)
        throw new ArgumentOutOfRangeException("age", "Age cannot be less than zero.");
}
catch (ArgumentOutOfRangeException ex)
{
    Console.WriteLine("Exception caught: " + ex.Message);
}

5. Custom Exceptions

Sometimes the built-in exceptions don’t quite fit your needs. That’s when you can create your own custom exceptions by extending the Exception class. This helps you provide clearer, more meaningful error messages and error handling logic specific to your application's domain.

5.1 Defining a Custom Exception

public class InvalidUserInputException : Exception
{
    public InvalidUserInputException() { }

    public InvalidUserInputException(string message)
        : base(message) { }

    public InvalidUserInputException(string message, Exception inner)
        : base(message, inner) { }
}

This class can now be thrown and caught just like any other exception:

try
{
    string input = "";
    if (string.IsNullOrWhiteSpace(input))
    {
        throw new InvalidUserInputException("Input cannot be empty.");
    }
}
catch (InvalidUserInputException ex)
{
    Console.WriteLine("Custom exception caught: " + ex.Message);
}

6. Best Practices for Exception Handling

Good exception handling improves your code’s reliability and readability. Follow these best practices to get the most out of C# exception handling:

  • Avoid empty catch blocks – Silently swallowing exceptions can lead to harder-to-find bugs.
  • Catch specific exceptions first – Catching general Exception before more specific ones can mask errors.
  • Use finally for cleanup – Always release resources in a finally block or with using statements.
  • Don’t use exceptions for flow control – Exceptions should not replace conditional logic.
  • Log errors meaningfully – Use structured logging tools (like Serilog, NLog) to track exceptions.

7. Sample Program Demonstrating All Concepts

using System;

public class InvalidUserInputException : Exception
{
    public InvalidUserInputException(string message) : base(message) { }
}

class Program
{
    static void Main()
    {
        try
        {
            Console.Write("Enter your age: ");
            string input = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(input))
                throw new InvalidUserInputException("Age input cannot be empty.");

            int age = int.Parse(input);
            if (age < 0)
                throw new ArgumentOutOfRangeException("Age cannot be negative.");

            Console.WriteLine($"Your age is: {age}");
        }
        catch (InvalidUserInputException ex)
        {
            Console.WriteLine("Input Error: " + ex.Message);
        }
        catch (FormatException)
        {
            Console.WriteLine("Please enter a valid number.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Unexpected error: " + ex.Message);
        }
        finally
        {
            Console.WriteLine("Execution completed.");
        }
    }
}

8. Conclusion

Exception handling in C# is a critical part of robust software development. With proper use of try-catch-finally and custom exceptions, developers can prevent applications from crashing, give users informative feedback, and manage errors gracefully. Knowing when and how to handle exceptions ensures smoother execution, better debugging, and a much cleaner user experience.

FAQs

1. What is the difference between try-catch and try-finally?

try-catch handles exceptions; try-finally is used when cleanup is needed regardless of success or failure.

2. Can I catch multiple exceptions in a single catch block?

Yes, in C# 6 and above, you can use pattern matching like catch (Exception ex) when (...) to filter conditions.

3. What is a custom exception?

A custom exception is a user-defined error type created by extending the Exception class to provide specific error semantics.

4. When should I throw an exception?

Throw exceptions when your program encounters an unexpected or erroneous situation that it cannot resolve without user or system intervention.

5. Should I catch exceptions in every method?

No, only catch exceptions where you can handle them meaningfully. Let them bubble up otherwise and handle them at higher levels.

Post a Comment

Post a Comment (0)

Previous Post Next Post

ads

ads

Update cookies preferences