What happens If you put a more general exception type before a more specific one?
the compiler will give you an error like:
A previous catch clause already catches all exceptions of this or a super type
For example:
catch (Exception ex) // general
catch (NullReferenceException ex) // specific
❌ This won’t compile, because the NullReferenceException will never be reached (since Exception already covers it).
what happens here
try
{
throw new NullReferenceException();
}
catch (NullReferenceException ex)
{
Console.WriteLine(“Caught NullReferenceException”);
}
catch (ArgumentException ex)
{
Console.WriteLine(“Caught ArgumentException”);
}
catch (Exception ex)
{
Console.WriteLine(“Caught general Exception”);
}
C# checks the catch blocks in order, top to bottom.
When it finds the first matching type, it executes that block.
Here, since you threw a NullReferenceException, it matches the first catch (NullReferenceException ex) and enters there.
The later catch blocks (ArgumentException, Exception) are skipped.
✅ So, in your scenario:
NullReferenceException → enters first catch.
If it were ArgumentException → enters the second catch.
If it were something else (e.g. InvalidOperationException) → falls through to the last catch (Exception).
what is the purpose of when keyword in exception handling?
In C#, you can add a filter to a catch block like this:
try
{
// some code
}
catch (Exception ex) when (ex.Message.Contains(“timeout”))
{
// Handle only timeout-related exceptions
}
catch (Exception ex)
{
// Handle all other exceptions
}
The when filter is evaluated before the catch block is chosen.
If the filter evaluates to true, that catch handles the exception.
If it evaluates to false, the runtime will continue searching for another matching catch block, as if that block didn’t exist.
This is different from putting an if inside the catch block, because with when:
The stack trace isn’t altered (since the exception is only “caught” if the filter passes).
The runtime can continue looking for other matches instead of forcing you to rethrow.
Filters also let you write more precise exception handling, without nesting lots of if checks inside the catch.
So yes, it absolutely affects which block is chosen: the runtime goes top-to-bottom through the catch list, but only picks the first one where both the type matches and the filter evaluates to true.
What happens when the below code executes?
try
{
throw new InvalidOperationException(“timeout error”);
}
catch (InvalidOperationException ex) when (ex.Message.Contains(“disk”))
{
Console.WriteLine(“Handled disk issue”);
}
catch (InvalidOperationException ex) when (ex.Message.Contains(“timeout”))
{
Console.WriteLine(“Handled timeout issue”);
}
catch (Exception ex)
{
Console.WriteLine(“Handled general exception”);
}
Exception is thrown
throw new InvalidOperationException(“timeout error”);
Runtime scans catch blocks in order
First block: catch (InvalidOperationException ex) when (ex.Message.Contains(“disk”))
Type matches (InvalidOperationException).
Filter is evaluated: “timeout error”.Contains(“disk”) → false.
Because the filter is false, this block is skipped.
Next block
Second block: catch (InvalidOperationException ex) when (ex.Message.Contains(“timeout”))
Type matches.
Filter is evaluated: “timeout error”.Contains(“timeout”) → true.
Filter passes → this block handles the exception.
“Handled timeout issue” is printed.
Other blocks are ignored
The catch (Exception ex) block is never reached because the exception was already handled.
Important things to highlight
Filters decide before entry: If a filter fails, it’s as if the block doesn’t exist.
Stack trace preservation: Because the exception is only “caught” if the filter passes, the original stack trace isn’t reset (unlike catching and rethrowing).
Fallback: If all filters return false, the runtime keeps searching down until it finds another matching catch (or crashes if none exist).
👉 This makes when filters super useful for fine-grained exception handling without deep nesting.
Do you want me to also contrast this with the alternative (if inside the catch) so you can clearly show why when is better?
How to preserve stack trace even if you catch the exception?
If you do just throw; inside a catch, the stack trace is preserved.
If you do throw ex;, the stack trace is reset to the current point (you lose the original call info).
Example
try
{
DangerousMethod();
}
catch (Exception ex)
{
Console.WriteLine(“Logging exception…”);
throw; // preserves original stack trace
}
✅ The stack trace still points to the line in DangerousMethod() where the exception originated.
Common pitfalls
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex; // ❌ resets the stack trace
}
Now the stack trace shows only the rethrow location, not where it originally happened.
Best practices to preserve stack trace
Use throw; instead of throw ex;
This is the simplest and safest.
Use ExceptionDispatchInfo if you need to rethrow later
If you want to capture an exception and rethrow it somewhere else (not immediately), you can use System.Runtime.ExceptionServices.ExceptionDispatchInfo:
using System;
using System.Runtime.ExceptionServices;
try
{
DangerousMethod();
}
catch (Exception ex)
{
var edi = ExceptionDispatchInfo.Capture(ex);
// do some work, maybe pass edi around
edi.Throw(); // preserves the original stack trace
}
👉 So the answer is:
Use plain throw; inside the same catch block.
Use ExceptionDispatchInfo.Capture(…).Throw(); if you need to rethrow later, while still preserving the original stack trace.
What does a when filter in a catch block do in C#?
It evaluates a condition before the catch block is chosen; if false, the block is skipped.
How is a when filter different from an if inside a catch?
With when, the exception isn’t caught unless the filter passes, so no rethrow is needed and the stack trace isn’t altered.
What happens if the when filter evaluates to false?
The runtime continues searching for another matching catch block.
Why are when filters stack-trace friendly?
Because the exception is only caught if the filter passes, so the stack trace remains untouched if skipped.
How do you preserve the stack trace when rethrowing inside the same catch?
Use throw; instead of throw ex;.
What’s the problem with throw ex;?
It resets the stack trace to the rethrow point, losing the original call location.
How can you preserve the stack trace if you want to rethrow the exception later (not immediately)?
Use ExceptionDispatchInfo.Capture(ex) and later call edi.Throw().
Do you need to wrap the exception when using ExceptionDispatchInfo.Capture?
No, it captures the existing exception and its stack trace; you just call edi.Throw() to rethrow it.
What’s the difference between throw; and ExceptionDispatchInfo.Throw()?
throw; only works inside the same catch, while ExceptionDispatchInfo.Throw() works anywhere and still preserves the original stack trace.
What is StringBuilder?
A mutable sequence of characters in C# that allows efficient string manipulation.
Why use StringBuilder instead of a string?
Strings are immutable. Concatenating strings repeatedly creates new objects, while StringBuilder modifies the same buffer.
How do you create a StringBuilder?
new StringBuilder(), new StringBuilder(“Hello”), or new StringBuilder(50) (initial capacity).
What happens if the buffer exceeds capacity?
A larger buffer is allocated, old content is copied, and new content is added
Why is StringBuilder memory efficient?
Modifies the internal buffer and only creates a string on ToString(), reducing memory allocations and GC pressure.
When should you use StringBuilder?
In loops with many concatenations or repeated string modifications.
What are the advantages of using HttpClientFactory
Resilience: Integrates with Polly (retry, circuit breaker, fallback).
Centralized configuration for headers, base URLs, policies.
Logging + metrics built-in.
Avoids socket exhaustion by pooling handlers correctly.
What problem does HttpClientFactory solve?
It manages HttpClient lifetimes to prevent socket exhaustion and handle DNS refresh issues.
How do you register HttpClients?
services.AddHttpClient();