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 |
|
|
|
Recommended |
Avoid throwing directly for specific errors |
Use for meaningful domain errors |
Key Points
-
System.Exceptionis the base class of .NET exceptions. -
Application-specific exceptions inherit from
Exceptionor an appropriate derived exception. -
Custom exceptions make errors easier to identify and handle.
-
Use specific exception types instead of throwing a generic
Exceptionfor 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
-
InsufficientBalanceExceptioninherits fromException. -
The constructor passes the message to the base class.
-
The application throws the custom exception when the balance is insufficient.
-
The matching
catchblock 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]throwsIndexOutOfRangeException. -
The first
catchdoes not match. -
The second
catchhandles the exception. -
The remaining
catchblock is skipped.
Key Points
-
Multiple
catchblocks are allowed. -
Only the first matching
catchblock 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
-
The
tryblock throws"Original Error". -
Before the exception propagates, the
finallyblock executes. -
The
finallyblock throws"Finally Error". -
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
-
finallynormally executes whether an exception occurs or not. -
An exception inside
finallycan mask the original exception. -
Avoid throwing new exceptions from cleanup operations.
-
Handle cleanup failures carefully when necessary.
-
Prefer
usingfor standard resource disposal, but remember thatDispose()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
tryblock throws an exception. -
The first
catchchecks thewhencondition. -
Because the condition is true, the first
catchexecutes. -
If the condition were false, the next matching
catchwould be considered.
Key Points
-
whenprovides conditional exception handling. -
It avoids unnecessary nested
ifstatements 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 originalcatchblock.
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
-
The division operation throws an exception.
-
ExceptionDispatchInfo.Capture(ex)captures the exception's dispatch information. -
The exception is stored for later use.
-
captured.Throw()rethrows it outside the originalcatchblock while preserving the original stack trace.
Differences
|
Feature |
|
|
|---|---|---|
|
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 acatch. -
Use
ExceptionDispatchInfowhen an exception must be captured and rethrown later. -
Both preserve original stack trace information.
-
ExceptionDispatchInfois 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
-
AddProblemDetails()registers support for standardized HTTP error responses. -
UseExceptionHandler()adds centralized exception handling middleware. -
The endpoint throws an exception.
-
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 |
|
Specific exceptions |
Catch exceptions you can meaningfully handle |
|
Structured logging |
Log exceptions with contextual information |
|
Preserve stack trace |
Use |
|
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 |
|
Correlation ID |
Associate logs with a particular request |
Common HTTP Status Codes
|
Scenario |
HTTP Status |
|---|---|
|
Invalid request |
|
|
Unauthorized request |
|
|
Access denied |
|
|
Resource not found |
|
|
Business conflict |
|
|
Unexpected server exception |
|
|
Temporary service unavailability |
|
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
ProblemDetailsresponses. -
Implement retries only for transient failures, with limits and backoff.
Quick Interview Revision
|
Question |
Important Answer |
|---|---|
|
|
General error vs application-specific error |
|
How to create custom exceptions? |
Inherit from |
|
Multiple catch blocks? |
Yes, first matching catch executes |
|
Exception inside |
New exception can mask the original |
|
Exception filters? |
Conditional exception handling using |
|
|
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.