Exception Handling in C#: try-catch-finally, throw & Custom Exceptions

1. Exception Handling in C#

Q: What is exception handling in C#?

Exception handling is a mechanism to handle runtime errors (exceptions) gracefully, preventing program crashes and allowing recovery or logging. In C#, exceptions are objects derived from the System.Exception class, thrown when errors occur (e.g., division by zero, file not found).

Q: Why is exception handling important?

Q: How does exception handling in C# differ from C/C++?

2. try, catch, and finally

Q: What is the try block in C#?

The try block encloses code that might throw an exception. If an exception occurs, control transfers to a matching catch block.

Syntax:

try {
    // Code that might throw an exception
}

Q: What is the catch block in C#?

The catch block handles exceptions thrown in the try block. It can specify an exception type to catch specific errors or use a general Exception to catch all. Multiple catch blocks can handle different exception types.

Syntax:

catch (ExceptionType ex) {
    // Handle exception
}

Q: What is the finally block in C#?

The finally block executes regardless of whether an exception is thrown or caught, ensuring cleanup (e.g., closing files). It is optional and follows try or catch.

Syntax:

finally {
    // Cleanup code
}

Q: What is the throw statement in C#?

The throw statement raises an exception explicitly, either a new exception or rethrowing a caught one.

Syntax:

throw new Exception("Error message");

Q: Can you give an example of try, catch, and finally in C#?

using System;

namespace ExceptionHandling
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int[] numbers = { 1, 2, 3 };
                Console.WriteLine(numbers[5]); // Throws IndexOutOfRangeException
                int result = 10 / 0; // Throws DivideByZeroException
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine($"Index error: {ex.Message}");
            }
            catch (DivideByZeroException ex)
            {
                Console.WriteLine($"Division error: {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"General error: {ex.Message}");
            }
            finally
            {
                Console.WriteLine("Cleanup in finally block.");
            }
        }
    }
}

Output:

Index error: Index was outside the bounds of the array.
Cleanup in finally block.

3. Custom Exceptions

Q: What are custom exceptions in C#?

Custom exceptions are user-defined exception classes that inherit from System.Exception (or a derived class like ApplicationException). They allow developers to create specific error types for their application, improving clarity and handling.

Syntax:

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

Q: When should you use custom exceptions?

Q: Can you give an example of a custom exception in C#?

using System;

namespace CustomExceptionExample
{
    // Custom exception class
    class InsufficientBalanceException : Exception
    {
        public decimal CurrentBalance { get; }
        public decimal RequiredAmount { get; }

        public InsufficientBalanceException(string message, decimal balance, decimal amount)
            : base(message)
        {
            CurrentBalance = balance;
            RequiredAmount = amount;
        }
    }

    class BankAccount
    {
        private decimal balance;

        public BankAccount(decimal initialBalance)
        {
            balance = initialBalance;
        }

        public void Withdraw(decimal amount)
        {
            if (amount <= 0)
            {
                throw new ArgumentException("Withdrawal amount must be positive.");
            }
            if (amount > balance)
            {
                throw new InsufficientBalanceException(
                    $"Cannot withdraw {amount:C}. Insufficient funds.",
                    balance, amount);
            }
            balance -= amount;
            Console.WriteLine($"Withdrew {amount:C}. New balance: {balance:C}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            BankAccount account = new BankAccount(100.50m);

            try
            {
                account.Withdraw(50m); // Valid withdrawal
                account.Withdraw(200m); // Throws custom exception
            }
            catch (InsufficientBalanceException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
                Console.WriteLine($"Balance: {ex.CurrentBalance:C}, Requested: {ex.RequiredAmount:C}");
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine($"Argument error: {ex.Message}");
            }
            finally
            {
                Console.WriteLine("Transaction processing completed.");
            }
        }
    }
}

Output:

Withdrew $50.00. New balance: $50.50
Error: Cannot withdraw $200.00. Insufficient funds.
Balance: $50.50, Requested: $200.00
Transaction processing completed.

4. Common Mistakes & Best Practices

Q: Common mistakes?

try/catch/finally:

Custom Exceptions:

Q: Best practices?

try/catch/finally:

Custom Exceptions:

General: