Exception handling is a critical aspect of robust software development. It allows developers to manage and respond to runtime errors gracefully, ensuring that the application can recover or fail gracefully without crashing unexpectedly.

Key Concepts

  1. Exceptions: An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions.
  2. Try-Catch Block: Used to handle exceptions. Code that might throw an exception is placed inside a try block, and exception handling code is placed inside one or more catch blocks.
  3. Finally Block: A finally block contains code that is always executed, regardless of whether an exception was thrown or caught.
  4. Throwing Exceptions: You can throw exceptions using the throw keyword.
  5. Custom Exceptions: You can create your own exception classes to handle specific error conditions in your application.

Try-Catch Block

The try-catch block is the fundamental construct for handling exceptions in C#. Here is the basic syntax:

try
{
    // Code that may throw an exception
}
catch (ExceptionType1 ex)
{
    // Code to handle ExceptionType1
}
catch (ExceptionType2 ex)
{
    // Code to handle ExceptionType2
}
finally
{
    // Code that will always execute
}

Example

using System;

class Program
{
    static void Main()
    {
        try
        {
            int[] numbers = { 1, 2, 3 };
            Console.WriteLine(numbers[5]); // This will throw an IndexOutOfRangeException
        }
        catch (IndexOutOfRangeException ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
        finally
        {
            Console.WriteLine("This block is always executed.");
        }
    }
}

Explanation

  • Try Block: Contains code that might throw an exception.
  • Catch Block: Catches and handles the specific exception (IndexOutOfRangeException in this case).
  • Finally Block: Contains code that will always execute, regardless of whether an exception was thrown or not.

Throwing Exceptions

You can throw exceptions using the throw keyword. This is useful when you want to signal an error condition explicitly.

Example

using System;

class Program
{
    static void Main()
    {
        try
        {
            ValidateAge(15);
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
    }

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

Explanation

  • ValidateAge Method: Throws an ArgumentException if the age is less than 18.
  • Main Method: Calls ValidateAge and catches the ArgumentException.

Custom Exceptions

You can create custom exception classes to handle specific error conditions in your application.

Example

using System;

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

class Program
{
    static void Main()
    {
        try
        {
            ValidateAge(15);
        }
        catch (InvalidAgeException ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
    }

    static void ValidateAge(int age)
    {
        if (age < 18)
        {
            throw new InvalidAgeException("Age must be 18 or older.");
        }
    }
}

Explanation

  • InvalidAgeException Class: A custom exception class that inherits from Exception.
  • ValidateAge Method: Throws an InvalidAgeException if the age is less than 18.
  • Main Method: Calls ValidateAge and catches the InvalidAgeException.

Practical Exercises

Exercise 1: Basic Exception Handling

Task: Write a program that takes two integers as input and divides them. Handle any potential exceptions that might occur.

Solution:

using System;

class Program
{
    static void Main()
    {
        try
        {
            Console.Write("Enter the first number: ");
            int num1 = int.Parse(Console.ReadLine());

            Console.Write("Enter the second number: ");
            int num2 = int.Parse(Console.ReadLine());

            int result = num1 / num2;
            Console.WriteLine("Result: " + result);
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Input must be a valid integer.");
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine("Cannot divide by zero.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
        finally
        {
            Console.WriteLine("Program execution completed.");
        }
    }
}

Exercise 2: Custom Exception

Task: Create a custom exception called NegativeNumberException and use it to handle cases where a negative number is input.

Solution:

using System;

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

class Program
{
    static void Main()
    {
        try
        {
            Console.Write("Enter a positive number: ");
            int num = int.Parse(Console.ReadLine());

            if (num < 0)
            {
                throw new NegativeNumberException("Number must be positive.");
            }

            Console.WriteLine("You entered: " + num);
        }
        catch (NegativeNumberException ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Input must be a valid integer.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
        }
        finally
        {
            Console.WriteLine("Program execution completed.");
        }
    }
}

Common Mistakes and Tips

  • Not Catching Specific Exceptions: Always catch specific exceptions before catching the general Exception class.
  • Overusing Exceptions: Use exceptions for exceptional conditions, not for regular control flow.
  • Ignoring Exceptions: Always handle exceptions appropriately. Ignoring them can lead to unexpected behavior and difficult-to-debug issues.

Conclusion

Exception handling is a vital part of writing robust and maintainable C# applications. By understanding and using try-catch blocks, throwing exceptions, and creating custom exceptions, you can ensure that your application can handle errors gracefully and continue to function correctly. In the next module, we will explore more control structures, such as loops and switch statements, to further enhance your programming skills.

© Copyright 2024. All rights reserved