C# Exception Handling

Introduction

Exception handling in C# is a mechanism to handle runtime errors and maintain the normal flow of the application. C# provides a structured way to handle exceptions using try, catch, finally, and throw keywords. Proper exception handling ensures that your program can gracefully handle unexpected conditions and continue to operate or terminate cleanly.

Keywords Used in Exception Handling

  1. try: Encloses the block of code that might throw an exception.
  2. catch: Catches and handles exceptions thrown by the code inside the try block.
  3. finally: Executes a block of code regardless of whether an exception is thrown or not.
  4. throw: Used to explicitly throw an exception.

Basic Structure

try
{
    // Code that might throw an exception
}
catch (ExceptionType e)
{
    // Code to handle the exception
}
finally
{
    // Code that runs regardless of an exception
}

Example: Basic Exception Handling

using System;

namespace ExceptionHandlingExample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int a = 10;
                int b = 0;
                int result = a / b;
                Console.WriteLine($"Result: {result}");
            }
            catch (DivideByZeroException ex)
            {
                Console.WriteLine("Exception caught: Division by zero is not allowed.");
                Console.WriteLine($"Exception Message: {ex.Message}");
            }
            finally
            {
                Console.WriteLine("Execution of the finally block.");
            }
        }
    }
}

Output

Exception caught: Division by zero is not allowed.
Exception Message: Attempted to divide by zero.
Execution of the finally block.

Multiple Catch Blocks

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

Example

using System;
using System.IO;

namespace MultipleCatchExample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int[] numbers = { 1, 2, 3 };
                Console.WriteLine(numbers[5]);

                string path = "nonexistentfile.txt";
                string content = File.ReadAllText(path);
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine("Exception caught: Index out of range.");
                Console.WriteLine($"Exception Message: {ex.Message}");
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("Exception caught: File not found.");
                Console.WriteLine($"Exception Message: {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: General exception.");
                Console.WriteLine($"Exception Message: {ex.Message}");
            }
            finally
            {
                Console.WriteLine("Execution of the finally block.");
            }
        }
    }
}

Output

Exception caught: Index out of range.
Exception Message: Index was outside the bounds of the array.
Execution of the finally block.

Nested Try-Catch Blocks

You can nest try-catch blocks to handle exceptions in a more granular way.

Example

using System;

namespace NestedTryCatchExample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                try
                {
                    int a = 10;
                    int b = 0;
                    int result = a / b;
                    Console.WriteLine($"Result: {result}");
                }
                catch (DivideByZeroException ex)
                {
                    Console.WriteLine("Inner catch: Division by zero.");
                }

                int[] numbers = { 1, 2, 3 };
                Console.WriteLine(numbers[5]);
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine("Outer catch: Index out of range.");
            }
            finally
            {
                Console.WriteLine("Execution of the finally block.");
            }
        }
    }
}

Output

Inner catch: Division by zero.
Outer catch: Index out of range.
Execution of the finally block.

Throwing Exceptions

You can throw exceptions explicitly using the throw keyword.

Example

using System;

namespace ThrowExample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ValidateAge(15);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Console.WriteLine($"Exception caught: {ex.Message}");
            }
        }

        static void ValidateAge(int age)
        {
            if (age < 18)
            {
                throw new ArgumentOutOfRangeException("age", "Age must be at least 18.");
            }
            Console.WriteLine("Age is valid.");
        }
    }
}

Output

Exception caught: Age must be at least 18. (Parameter 'age')

Custom Exceptions

You can create custom exceptions by deriving from the Exception class.

Example

using System;

namespace CustomExceptionExample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ValidateAge(15);
            }
            catch (InvalidAgeException ex)
            {
                Console.WriteLine($"Exception caught: {ex.Message}");
            }
        }

        static void ValidateAge(int age)
        {
            if (age < 18)
            {
                throw new InvalidAgeException("Age must be at least 18.");
            }
            Console.WriteLine("Age is valid.");
        }
    }

    // Custom exception class
    public class InvalidAgeException : Exception
    {
        public InvalidAgeException(string message) : base(message)
        {
        }
    }
}

Output

Exception caught: Age must be at least 18.

Best Practices for Exception Handling

  1. Use Specific Exceptions: Catch specific exceptions rather than using a general Exception catch block.
  2. Do Not Swallow Exceptions: Avoid catching exceptions without handling them or logging them.
  3. Clean Up Resources: Use the finally block to clean up resources like file handles, database connections, etc.
  4. Log Exceptions: Always log exceptions to help with debugging and monitoring.
  5. Custom Exceptions: Create custom exceptions for application-specific error conditions.

Conclusion

Exception handling in C# is a robust feature that helps in managing runtime errors gracefully. By using try, catch, finally, and throw, you can ensure that your application can handle unexpected conditions and continue to operate or terminate cleanly. Proper exception handling is crucial for building reliable and maintainable applications.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top