Exception Handling

16 questions found

'throw;' re-throws the CURRENT exception exactly as caught, preserving its original stack trace all the way back to where it was FIRST thrown; 'throw ex;' treats it as throwing a NEW exception from THIS point, overwriting the stack trace and losing the original throw location — making the bare 'throw;' the correct choice when re-throwing after logging or partial handling.
try {
  DoSomethingRisky();
} catch (Exception ex) {
  LogError(ex);
  throw;     // CORRECT: preserves the original stack trace
  // throw ex; // WRONG: resets the stack trace to this line
}
Real-world example Logging an exception's details before re-throwing it, without losing the diagnostic value of the original stack trace.

Common follow-ups: Is there any legitimate reason you'd deliberately want to use 'throw ex;' instead of bare 'throw;'?

Fundamentals
An exception thrown inside an async method is captured and stored WITHIN the returned Task object (rather than thrown immediately at the call site); it's only actually re-thrown when the caller AWAITS that Task — this is why unobserved (never-awaited) Task exceptions can silently vanish, and why try/catch around an 'await' correctly catches exceptions from the awaited operation.
async Task<int> RiskyOperationAsync() {
  throw new InvalidOperationException("Failed");
}

try {
  int result = await RiskyOperationAsync(); // exception surfaces HERE, at the await
} catch (InvalidOperationException ex) {
  Console.WriteLine($"Caught: {ex.Message}");
}
Real-world example Understanding why an exception from a 'fire-and-forget' unwaited Task might silently disappear instead of crashing anything visibly.

Common follow-ups: What is an 'unobserved task exception,' and how did its default behavior change between older and newer .NET versions?

Asynchronous Programming
Task.WhenAll() wraps ALL exceptions from ALL failed tasks into a SINGLE AggregateException, accessible via its .InnerExceptions collection; however, when you 'await' the WhenAll() result directly, only the FIRST exception is actually re-thrown to you (as its own specific type) — you must inspect the ORIGINAL Task objects individually if you need every failure's details.
var task1 = Task.Run(() => throw new InvalidOperationException("Error 1"));
var task2 = Task.Run(() => throw new ArgumentException("Error 2"));
try {
  await Task.WhenAll(task1, task2); // only the FIRST exception surfaces here
} catch (Exception ex) {
  // To see ALL errors, check task1.Exception and task2.Exception individually
}
Real-world example Collecting and reporting EVERY failure from a batch of concurrent operations, not just the first one encountered.

Common follow-ups: How would you access the FULL AggregateException with all inner exceptions, rather than just the first one that 'await' surfaces?

Multithreading & Task Parallel Library
Since a 'when' filter's condition is evaluated BEFORE deciding whether to catch, you can put a logging call inside the filter expression itself (returning true always) to log EVERY exception as it passes through — even ones that ultimately AREN'T caught by this particular block — though many consider embedding side effects in a filter condition a code smell, since it obscures the filter's true purpose.
try {
  DoWork();
} catch (Exception ex) when (LogAndReturnFalse(ex)) {
  // never actually reached, since LogAndReturnFalse always returns false
}

bool LogAndReturnFalse(Exception ex) {
  Console.WriteLine($"Observed (but not handled): {ex.Message}");
  return false; // lets the exception continue propagating after logging
}
Real-world example Globally logging every exception that passes through a certain layer of code, even ones ultimately handled elsewhere further up the call stack.

Common follow-ups: What's a cleaner, less surprising alternative to achieve the same 'log everything, handle selectively' goal?

Asynchronous Programming
Register custom exception-handling middleware (or use the built-in UseExceptionHandler) early in the pipeline to catch ANY unhandled exception from later middleware/controllers, log it, and transform it into a consistent, structured error response (like the RFC 7807 Problem Details format) instead of leaking raw stack traces to API consumers.
app.UseExceptionHandler(errorApp => {
  errorApp.Run(async context => {
    var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>();
    context.Response.StatusCode = 500;
    context.Response.ContentType = "application/problem+json";
    await context.Response.WriteAsJsonAsync(new {
      title = "An unexpected error occurred",
      status = 500,
      detail = app.Environment.IsDevelopment() ? exceptionFeature?.Error.Message : null
    });
  });
});
Real-world example Ensuring a production API never leaks internal stack traces or exception details to external clients, while still logging full details server-side.

Common follow-ups: Why is it important to conditionally hide exception details based on the environment (Development vs Production)?

Fundamentals
Throwing an exception involves capturing a full stack trace and unwinding the call stack, which is SIGNIFICANTLY more expensive (often orders of magnitude slower) than a normal return value or conditional check — exceptions should be reserved for genuinely EXCEPTIONAL, unexpected conditions, not routine logic like 'no results found,' which should instead use return values (like a nullable result, or the Result<T,E> pattern).
// Expensive anti-pattern: using exceptions for expected, routine 'not found' logic
try {
  var user = repository.GetByIdOrThrow(id); // throws routinely when not found
} catch (NotFoundException) { return null; }

// Better: use a return value for an expected, routine outcome
var user = repository.TryGetById(id); // returns null, no exception overhead
Real-world example Refactoring a hot-path lookup method that routinely 'fails' (like a cache miss) to avoid the real performance cost of exception-based control flow.

Common follow-ups: Roughly how much slower is throwing/catching an exception compared to a simple conditional check, in relative terms?

Memory & Garbage Collection

Showing 11–16 of 16