← Back to Articles         
Exception Handling – Advanced Concepts

Exception Handling – Advanced Concepts

Published on 12 Oct 2026     8 min read C#
Exception Handling
 

C# Exception Handling – Advanced Concepts and Best Practices

1. What is the difference between Exception and application-specific exceptions?

Definition:

  • Exception: The base class for exceptions in .NET.

  • Application-specific exception: A custom exception representing a particular business or application error.

Example

// General exception
throw new Exception("Something went wrong");

// Application-specific exception
throw new InsufficientBalanceException(
    "Insufficient account balance");

Here, InsufficientBalanceException is a custom exception class defined by the application.

Differences

Feature

Exception

Application-specific exception

Purpose

General error representation

Specific application error

Meaning

Less descriptive

Business-specific

Handling

General catch

Specific catch

Example

Exception

InsufficientBalanceException

Recommended

Avoid throwing directly for specific errors

Use for meaningful domain errors

Key Points

  • System.Exception is the base class of .NET exceptions.

  • Application-specific exceptions inherit from Exception or an appropriate derived exception.

  • Custom exceptions make errors easier to identify and handle.

  • Use specific exception types instead of throwing a generic Exception for every error.

 

2. How do you create custom exceptions?

Definition: A custom exception is a user-defined exception class created by inheriting from the Exception class.

Example

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

Usage:

try
{
    decimal balance = 1000;
    decimal withdrawal = 2000;

    if (withdrawal > balance)
    {
        throw new InsufficientBalanceException(
            "Insufficient account balance");
    }
}
catch (InsufficientBalanceException ex)
{
    Console.WriteLine(ex.Message);
}

Output:

Insufficient account balance

Explanation

  1. InsufficientBalanceException inherits from Exception.

  2. The constructor passes the message to the base class.

  3. The application throws the custom exception when the balance is insufficient.

  4. The matching catch block handles it.

Flow Diagram

Withdrawal Request
        |
        v
Check Account Balance
        |
        v
Withdrawal > Balance?
       /       \
     No         Yes
     |           |
     v           v
  Process     Throw Custom
 Withdrawal    Exception
                 |
                 v
             catch block
                 |
                 v
           Display Message

Key Points

  • Custom exceptions inherit from Exception.

  • Use meaningful names ending with Exception.

  • Provide constructors accepting a message and, when useful, an inner exception.

  • Use custom exceptions for meaningful application error categories.

  • Not every business validation failure requires an exception; expected validation failures can use normal result handling.

 

3. Can multiple catch blocks handle different exception types?

Yes. C# supports multiple catch blocks to handle different exceptions separately.

Example

try
{
    int[] numbers = { 10, 20, 30 };

    Console.WriteLine(numbers[5]);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("Division error");
}
catch (IndexOutOfRangeException ex)
{
    Console.WriteLine("Invalid array index");
}
catch (Exception ex)
{
    Console.WriteLine("General error");
}

Output:

Invalid array index

Explanation

  • Accessing numbers[5] throws IndexOutOfRangeException.

  • The first catch does not match.

  • The second catch handles the exception.

  • The remaining catch block is skipped.

Key Points

  • Multiple catch blocks are allowed.

  • Only the first matching catch block executes.

  • Specific exception types must appear before general exception types.

  • catch (Exception ex) should normally be placed last.

  • Use separate catches when different exception types require different handling.

 

4. What happens when an exception occurs inside a finally block?

Definition: If an exception occurs inside a finally block, it propagates to the caller unless it is handled inside that block.

If another exception is already propagating, the new exception replaces the original exception as the one being propagated.

Example

try
{
    throw new Exception("Original Error");
}
finally
{
    throw new Exception("Finally Error");
}

Result:

Unhandled Exception: Finally Error

Explanation

  1. The try block throws "Original Error".

  2. Before the exception propagates, the finally block executes.

  3. The finally block throws "Finally Error".

  4. The new exception replaces the original exception in the propagation path.

Flow Diagram

try block
    |
    v
Original Exception
    |
    v
finally block
    |
    v
New Exception
    |
    v
New Exception Propagates
    |
    v
Original Exception Masked

Key Points

  • finally normally executes whether an exception occurs or not.

  • An exception inside finally can mask the original exception.

  • Avoid throwing new exceptions from cleanup operations.

  • Handle cleanup failures carefully when necessary.

  • Prefer using for standard resource disposal, but remember that Dispose() itself can also throw.

 

5. What are exception filters using when?

Definition: Exception filters allow a catch block to handle an exception only when an additional condition is satisfied.

The when keyword is used to specify the condition.

Example

try
{
    int errorCode = 404;

    throw new Exception("Resource not found");
}
catch (Exception ex) when (ex.Message == "Resource not found")
{
    Console.WriteLine("Handled resource error");
}
catch (Exception ex)
{
    Console.WriteLine("Other error");
}

Output:

Handled resource error

Explanation

  • The try block throws an exception.

  • The first catch checks the when condition.

  • Because the condition is true, the first catch executes.

  • If the condition were false, the next matching catch would be considered.

Key Points

  • when provides conditional exception handling.

  • It avoids unnecessary nested if statements inside catch blocks.

  • Commonly used with exception properties such as error codes.

  • A filter is evaluated before the exception handler is entered.

  • Prefer checking structured properties instead of comparing exception messages.

Real-world example:

catch (SqlException ex) when (ex.Number == 1205)
{
    Console.WriteLine("SQL Server deadlock detected");
}

This handles SQL Server deadlock errors specifically. Retry logic should still be bounded and appropriate for the operation.

 

6. What is the difference between throw and ExceptionDispatchInfo.Throw()?

Definition:

  • throw; rethrows the currently handled exception while preserving its stack trace.

  • ExceptionDispatchInfo.Throw() rethrows a captured exception while preserving its original exception information, even outside the original catch block.

Example

using System.Runtime.ExceptionServices;

ExceptionDispatchInfo? captured = null;

try
{
    int zero = 0;
    int result = 10 / zero;
}
catch (Exception ex)
{
    captured = ExceptionDispatchInfo.Capture(ex);
}

// Rethrow outside catch
captured?.Throw();

Explanation

  1. The division operation throws an exception.

  2. ExceptionDispatchInfo.Capture(ex) captures the exception's dispatch information.

  3. The exception is stored for later use.

  4. captured.Throw() rethrows it outside the original catch block while preserving the original stack trace.

Differences

Feature

throw;

ExceptionDispatchInfo.Throw()

Rethrow inside catch

Yes

Yes

Rethrow outside catch

No

Yes

Preserve original stack trace

Yes

Yes

Requires capture

No

Usually

Common use

Normal exception handling

Deferred exception propagation

Key Points

  • Use throw; for normal rethrowing inside a catch.

  • Use ExceptionDispatchInfo when an exception must be captured and rethrown later.

  • Both preserve original stack trace information.

  • ExceptionDispatchInfo is useful in infrastructure libraries and advanced asynchronous processing.

  • ExceptionDispatchInfo.Throw(ex) also provides a static overload for throwing an exception while preserving its existing stack trace information.

 

7. What are best practices for exception handling in enterprise applications?

Definition: Enterprise exception handling focuses on centralized error processing, structured logging, security, and consistent error responses.

Example: ASP.NET Core Web API

In modern ASP.NET Core, use centralized exception handling rather than repeating try-catch in every controller.

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();

app.MapGet("/test", () =>
{
    throw new InvalidOperationException(
        "Unexpected application error");
});

app.Run();

Explanation

  1. AddProblemDetails() registers support for standardized HTTP error responses.

  2. UseExceptionHandler() adds centralized exception handling middleware.

  3. The endpoint throws an exception.

  4. The middleware handles the unhandled exception and can generate a standardized error response.

In production, configure exception logging and mappings for known exception types. Unexpected errors should generally return HTTP 500 without exposing internal details.

Flow Diagram

Client Request
      |
      v
Exception Middleware
      |
      v
Web API Controller
      |
      v
Service Layer
      |
      v
Repository / Database
      |
      v
Exception Occurs
      |
      v
Exception Middleware
      |
      +----> Log Exception
      |
      +----> Map HTTP Status
      |
      v
Standard Error Response
      |
      v
Client

Enterprise Best Practices

Practice

Explanation

Centralized handling

Use middleware or IExceptionHandler

Specific exceptions

Catch exceptions you can meaningfully handle

Structured logging

Log exceptions with contextual information

Preserve stack trace

Use throw;, not throw ex;

Custom exceptions

Represent meaningful application failures

Secure responses

Do not expose stack traces or sensitive data

Correct HTTP status

Map known errors to appropriate status codes

Avoid empty catches

Never silently swallow unexpected errors

Resource cleanup

Prefer using and await using

Correlation ID

Associate logs with a particular request

Common HTTP Status Codes

Scenario

HTTP Status

Invalid request

400 Bad Request

Unauthorized request

401 Unauthorized

Access denied

403 Forbidden

Resource not found

404 Not Found

Business conflict

409 Conflict

Unexpected server exception

500 Internal Server Error

Temporary service unavailability

503 Service Unavailable

Key Points

  • Use global exception handling for unexpected errors in ASP.NET Core Web API.

  • Handle expected validation errors without unnecessarily throwing exceptions.

  • Log errors with correlation IDs and relevant request context.

  • Avoid logging the same exception repeatedly at multiple layers.

  • Never expose database connection strings, secrets, or internal stack traces to clients.

  • Use appropriate HTTP status codes and consistent ProblemDetails responses.

  • Implement retries only for transient failures, with limits and backoff.

 

Quick Interview Revision

Question

Important Answer

Exception vs custom exception

General error vs application-specific error

How to create custom exceptions?

Inherit from Exception

Multiple catch blocks?

Yes, first matching catch executes

Exception inside finally?

New exception can mask the original

Exception filters?

Conditional exception handling using when

throw; vs ExceptionDispatchInfo.Throw()

Immediate rethrow vs captured/deferred rethrow

Enterprise best practice?

Centralized handling, structured logging, secure responses

Interview takeaway: In enterprise ASP.NET Core applications, use centralized exception handling for unexpected errors, specific exception types for meaningful failures, and structured logging for troubleshooting. Keep exception details in server-side logs and return safe, consistent error responses to clients.

← Back to Articles