Custom Exceptions in C#
What are Custom Exceptions in C#?
        A Custom Exception in C# is a user-defined exception class that extends the built-in Exception class. It allows developers to create meaningful error messages specific to their application’s needs.
    
Key Features of Custom Exceptions
- Extends the 
Exceptionclass to define application-specific errors. - Allows meaningful error messages that improve debugging.
 - Can include additional properties and methods for error handling.
 - Supports 
InnerExceptionfor preserving original exceptions. 
Example: Defining and Throwing a Custom Exception
The following example demonstrates how to define and throw a custom exception.
Example: Creating a Custom Exception for Invalid Age
using System;
public class InvalidAgeException : Exception
{
    public InvalidAgeException(string message) : base(message) { }
}
// Usage
class Program
{
    static void Main()
    {
        try
        {
            int age = 15;
            if (age < 18)
            {
                throw new InvalidAgeException("Age must be 18 or older.");
            }
            Console.WriteLine("Age is valid.");
        }
        catch (InvalidAgeException ex)
        {
            Console.WriteLine($"Custom Exception Caught: {ex.Message}");
        }
    }
}
// Output:
// Custom Exception Caught: Age must be 18 or older.
        
        
    
        This example defines an InvalidAgeException class and throws it when an invalid age is encountered.
    
Adding Properties to a Custom Exception
Custom exceptions can include additional properties to provide more details about the error.
Example: Adding a Custom Property to an Exception
using System;
public class ProductNotFoundException : Exception
{
    public int ProductId { get; }
    public ProductNotFoundException(int productId, string message) : base(message)
    {
        ProductId = productId;
    }
}
// Usage
class Program
{
    static void Main()
    {
        try
        {
            int productId = 101;
            throw new ProductNotFoundException(productId, $"Product ID {productId} not found.");
        }
        catch (ProductNotFoundException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            Console.WriteLine($"Product ID: {ex.ProductId}");
        }
    }
}
// Output:
// Error: Product ID 101 not found.
// Product ID: 101
        
        
    
        The ProductNotFoundException class includes a ProductId property to store additional error details.
    
Using InnerException in Custom Exceptions
        Custom exceptions can include an InnerException to preserve the original cause of an error.
    
Example: Wrapping an Inner Exception
using System;
public class FileProcessingException : Exception
{
    public FileProcessingException(string message, Exception innerException)
        : base(message, innerException) { }
}
// Usage
class Program
{
    static void Main()
    {
        try
        {
            try
            {
                throw new System.IO.FileNotFoundException("File not found.");
            }
            catch (System.IO.FileNotFoundException ex)
            {
                throw new FileProcessingException("Error processing the file.", ex);
            }
        }
        catch (FileProcessingException ex)
        {
            Console.WriteLine($"Custom Exception: {ex.Message}");
            Console.WriteLine($"Inner Exception: {ex.InnerException?.Message}");
        }
    }
}
// Output:
// Custom Exception: Error processing the file.
// Inner Exception: File not found.
        
        
    
        The FileProcessingException class wraps the original FileNotFoundException, keeping the original error details intact.
    
Best Practices for Using Custom Exceptions
- Only create custom exceptions when built-in exceptions are not sufficient.
 - Always inherit from the 
Exceptionclass and provide meaningful error messages. - Include relevant properties to store additional error details.
 - Use 
InnerExceptionwhen wrapping another exception. - Log both the custom and inner exceptions to maintain full error details.