C# > Advanced C# > Exception Handling > Using System.Exception

Custom Exception Class with Logging

This snippet demonstrates how to create a custom exception class that inherits from `System.Exception`. It includes properties for additional error information and uses logging for better diagnostics.

Code Snippet

This code defines a custom exception class, `CustomException`, which inherits from `System.Exception`. It includes an `ErrorCode` property to store a specific error identifier and a `Timestamp` property to record when the exception occurred. The constructor takes a message and error code, and it calls `LogException` to write the error to a file. The `Example` class demonstrates how to throw and catch the custom exception, along with a general exception handler. The logging mechanism is implemented to provide additional details about the error for debugging purposes.

using System;
using System.IO;

public class CustomException : Exception
{
    public int ErrorCode { get; set; }
    public DateTime Timestamp { get; set; }

    public CustomException(string message, int errorCode) : base(message)
    {
        ErrorCode = errorCode;
        Timestamp = DateTime.Now;
        LogException(message, errorCode);
    }

    public CustomException(string message, int errorCode, Exception innerException) : base(message, innerException)
    {
        ErrorCode = errorCode;
        Timestamp = DateTime.Now;
        LogException(message, errorCode);
    }

    private void LogException(string message, int errorCode)
    {
        string logFilePath = "error.log";
        try
        {
            using (StreamWriter writer = new StreamWriter(logFilePath, true))
            {
                writer.WriteLine($"[{Timestamp}] Error Code: {errorCode}, Message: {message}");
            }
        }
        catch (Exception logException)
        {
            Console.WriteLine($"Failed to log exception: {logException.Message}");
        }
    }
}

public class Example
{
    public static void Main(string[] args)
    {
        try
        {
            // Simulate an error condition
            int numerator = 10;
            int denominator = 0;
            if (denominator == 0)
            {
                throw new CustomException("Division by zero attempted.", 1001);
            }
            int result = numerator / denominator;
            Console.WriteLine($"Result: {result}");
        }
        catch (CustomException ex)
        {
            Console.WriteLine($"Custom Exception caught: {ex.Message}, Error Code: {ex.ErrorCode}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"General Exception caught: {ex.Message}");
        }
    }
}

Concepts Behind the Snippet

The primary concept is to extend the built-in `System.Exception` class to create more specific exception types that can carry additional information relevant to the application's domain. This allows for more granular error handling and easier debugging. By implementing logging within the exception class, we centralize the error reporting, making it simpler to track and resolve issues. The use of `try-catch` blocks demonstrates exception handling, where code that might throw an exception is wrapped in a `try` block, and the `catch` block handles the exception if it occurs.

Real-Life Use Case

Imagine a banking application where various operations, such as withdrawals or transfers, can fail due to insufficient funds, invalid account numbers, or network issues. Creating custom exceptions like `InsufficientFundsException`, `InvalidAccountException`, and `NetworkException` allows the application to handle these specific scenarios differently. Each exception can include relevant data, such as the account number or the amount involved, and logging this information helps in auditing and debugging.

Best Practices

  • Create specific exception types: Avoid throwing generic `Exception` objects. Create custom exception classes that represent specific error conditions in your application.
  • Include relevant data: Add properties to your custom exception classes to store additional information about the error.
  • Log exceptions: Log exceptions to a file or database for later analysis.
  • Use inner exceptions: If an exception is caught and re-thrown, include the original exception as the inner exception to preserve the call stack.
  • Follow exception handling principles: Only catch exceptions that you can handle. If you can't handle an exception, let it propagate up the call stack.

Interview Tip

When discussing exception handling in interviews, highlight the importance of creating custom exceptions for domain-specific errors. Explain how this improves code readability, maintainability, and error handling. Be prepared to discuss the benefits of logging exceptions and the role of inner exceptions in preserving the call stack.

When to Use Them

Use custom exceptions when you need to represent specific error conditions in your application that the standard exception types do not adequately cover. This is particularly important in complex systems where detailed error information is crucial for debugging and maintaining the application.

Memory Footprint

Custom exceptions increase the memory footprint slightly due to the additional properties and the logging mechanism. However, this overhead is typically negligible compared to the benefits of improved error handling and debugging. Consider the frequency of exception throwing when evaluating memory impact.

Alternatives

Alternatives to custom exceptions include using standard exception types with custom error codes or using a more generic error reporting mechanism. However, these approaches often lack the specificity and clarity that custom exceptions provide. Another alternative is using Result objects to represent the outcome of an operation, avoiding exceptions for expected error scenarios.

Pros

  • Improved code readability: Custom exceptions make it easier to understand the error conditions that can occur in your application.
  • Enhanced error handling: Custom exceptions allow you to handle specific error scenarios differently.
  • Better debugging: The additional information included in custom exceptions makes it easier to debug errors.

Cons

  • Increased code complexity: Creating custom exceptions adds to the overall complexity of the code.
  • Potential performance overhead: Throwing and catching exceptions can be relatively expensive, especially if they occur frequently.

FAQ

  • Why should I create custom exception classes?

    Custom exception classes provide a way to represent specific error conditions in your application more clearly than generic exception types. They allow you to include additional information about the error and handle specific error scenarios differently.
  • How do I log exceptions?

    You can log exceptions to a file, database, or other logging system. The example code demonstrates logging exceptions to a file using `StreamWriter`. Consider using a logging library like NLog or Serilog for more advanced logging features.
  • What is an inner exception?

    An inner exception is the original exception that caused another exception to be thrown. It preserves the call stack and provides valuable information for debugging. When catching and re-throwing an exception, always include the original exception as the inner exception.