Exception Handling
try/catch/finally, multiple catch order, throw vs throw ex, custom exceptions, and using.
6 questions
JuniorTheoryVery commonWhat roles do try, catch, and finally play in structured exception handling?
What roles do try, catch, and finally play in structured exception handling?
try wraps the code that might fail. When an exception is thrown, the runtime searches the catch blocks for one whose declared type matches, and runs that handler. finally runs afterwards whether or not an exception occurred, so it is where you put cleanup. A try needs at least one catch or finally.
Common mistakes
- ✗Assuming
finallyruns only when an exception was thrown, rather than always - ✗Writing a general
catch (Exception)that hides bugs by swallowing every failure - ✗Forgetting a
tryis invalid without at least onecatchorfinally
Follow-up questions
- →What happens if an exception is thrown but no catch block matches its type?
- →Can a finally block itself throw, and what happens to the original exception?
JuniorTheoryCommonWhat does the using statement do, and which types can it be used with?
What does the using statement do, and which types can it be used with?
using calls Dispose() on a resource deterministically when control leaves the scope, even on an exception, by compiling to a hidden try/finally. The type must implement IDisposable. It is for unmanaged or scarce resources like files, sockets, and DB connections. The declaration form (using var f = ...;) disposes at the end of the enclosing block.
Common mistakes
- ✗Thinking the
usingstatement is the same as a namespaceusingdirective - ✗Believing it works on any type, not only ones implementing
IDisposable - ✗Assuming
Dispose()is skipped when the block exits via an exception
Follow-up questions
- →How does a using declaration differ from the classic using statement in scope?
- →What is the difference between Dispose and a finalizer for releasing resources?
MiddleTheoryCommonHow do you define a custom exception, and when is creating one worthwhile?
How do you define a custom exception, and when is creating one worthwhile?
Derive directly from Exception (not the obsolete ApplicationException), provide the standard constructors — parameterless, message, and message plus inner exception — and add properties carrying domain context. Create one only when callers need to catch and handle this specific failure differently; otherwise reuse a built-in type like ArgumentException or InvalidOperationException.
Common mistakes
- ✗Deriving from the obsolete
ApplicationExceptioninstead ofException - ✗Omitting the message and inner-exception constructors, losing wrapping context
- ✗Inventing a custom type when a built-in like
ArgumentExceptionalready fits
Follow-up questions
- →Why should a custom exception preserve the original error as an inner exception?
- →When is throwing a built-in exception type better than defining your own?
MiddleTheoryCommonWhen does a finally block run, and is there any case where it is skipped?
When does a finally block run, and is there any case where it is skipped?
finally runs whether or not an exception was thrown, and even when try or catch executes a return — the runtime runs finally before control actually leaves. That makes it the reliable place for cleanup. It is skipped only on hard process termination, such as Environment.FailFast, an uncatchable StackOverflowException, or the OS killing the process.
Common mistakes
- ✗Believing
finallyis skipped when an exception propagates out of the try - ✗Thinking a
returninside try or catch silently bypasses the finally block - ✗Assuming
finallyalways runs even on FailFast or a stack overflow
Follow-up questions
- →If both try and finally execute a return, which returned value actually wins?
- →Why can a long-running finally delay or block exception propagation upward?
MiddleTheoryOccasionalWith several catch blocks on one try, how does the runtime pick which one runs?
With several catch blocks on one try, how does the runtime pick which one runs?
The catch blocks are tested top to bottom, and the first one whose type matches the thrown exception wins — only that single handler runs, then control passes to finally. So more-specific types must come before more-general ones. If a broad catch like Exception precedes a narrower one, the narrower block is unreachable and the compiler reports an error.
Common mistakes
- ✗Placing
catch (Exception)first, making the specific blocks below unreachable - ✗Expecting several matching catch blocks to all run for one exception
- ✗Thinking the runtime chooses the closest type match rather than the first in order
Follow-up questions
- →How does an exception filter (
catch when (...)) change which block is selected? - →What happens when no catch block matches the thrown exception's type?
SeniorTheoryOccasionalInside a catch, how do throw; and throw ex; differ, and which should you prefer?
Inside a catch, how do throw; and throw ex; differ, and which should you prefer?
Inside a catch, throw; rethrows the same exception and preserves its original stack trace, so the frames pointing at the real origin survive. throw ex; throws the caught object as if new, resetting the stack trace to the rethrow line and erasing where the fault actually began. Prefer throw;; reach for throw ex; essentially never, since it hides the origin.
Common mistakes
- ✗Using
throw ex;to rethrow, resetting and losing the original stack trace - ✗Believing
throw;andthrow ex;behave identically at runtime - ✗Thinking
throw;swallows the exception rather than rethrowing it upward
Follow-up questions
- →How does wrapping in a new exception with an inner exception compare to a bare rethrow?
- →What does ExceptionDispatchInfo.Capture add over a plain throw; rethrow?