Dataslope logoDataslope

Error Handling

How C# represents things that go wrong — and how to respond without crashing the program

So far our programs have assumed everything works: files exist, inputs parse, math never overflows, networks never go down. Real programs aren't that lucky.

C# splits "things that go wrong" into two camps:

  1. Expected failures that you can predict and check for — these should be handled with regular control flow (return values, TryParse, bool flags).
  2. Exceptional failures that you can't easily prevent and shouldn't have to write a defensive check for at every call site — these use the exception system.

The discipline of knowing which is which is most of what "good error handling" means.

Exceptions, the mechanism

An exception is an object that represents a failure. When code throws one, normal execution stops, and the runtime unwinds the call stack looking for a matching catch block. If no catch is found, the program crashes.

Code Block
C# 13

Without the try/catch, the program would crash with a stack trace. The catch block lets us respond instead.

The flow of an exception

The exception travels up the stack until something catches it. Anything in its path (open files, half-built objects) gets cleaned up by stack unwinding plus finally blocks.

try / catch / finally

Code Block
C# 13

Rules:

  • The most specific catch blocks should come first. The runtime picks the first matching one.
  • A finally block always runs, whether or not an exception was thrown — perfect for cleanup.

Throwing your own exceptions

When your method receives nonsense input or hits an impossible state, the right thing to do is usually to throw, not to silently return wrong data.

Code Block
C# 13

A few common built-in exception types:

ExceptionUse when…
ArgumentExceptionAn argument is invalid for some reason
ArgumentNullExceptionA required argument was null
ArgumentOutOfRangeExceptionA number/index is outside an allowed range
InvalidOperationExceptionThe object is in a state where this call isn't allowed
NotImplementedException"I plan to write this method, but haven't yet"
FormatExceptionA string couldn't be parsed
KeyNotFoundExceptionA dictionary key didn't exist

Exceptions are NOT for ordinary control flow

This is a really common beginner trap:

Code Block
C# 13

It works, but it's wasteful and obscures intent. The whole point of TryParse is to not throw for the perfectly ordinary case "this string isn't a number":

Code Block
C# 13

Rule of thumb: if the failure is normal and frequent, use a return value. Use exceptions only for situations the caller shouldn't have to expect at every call site.

Catching the right thing

Avoid catching Exception (or worse, swallowing all exceptions silently) unless you're at the very top of the program and just want to log and exit gracefully. In ordinary code, catch only the specific exceptions you actually know how to handle.

Code Block
C# 13

A silent catch block is one of the most dangerous things in any codebase. It turns bugs into mysterious wrong answers.

Re-throwing

If you catch an exception only to log or annotate it but don't actually know how to recover, re-throw:

Code Block
C# 13

throw; re-throws the same exception. throw ex; looks similar but loses the original stack trace, making debugging harder.

Null and NullReferenceException

The single most common runtime error in C# is calling a method on a variable that turned out to be null:

Code Block
C# 13

Modern C# (with nullable reference types) lets the compiler warn you when a variable could be null — making NullReferenceException largely a thing of the past if you pay attention to the warnings.

Practice

Challenge
C# 13
Safe number summer

Implement SafeMath.SumNumbers(string[] inputs) which:

  • Parses each string as an int.
  • Skips any string that can't be parsed (do NOT throw).
  • Returns the sum of the ones that parsed successfully.

Program.cs calls it with {"10", "x", "20", "y", "5"} and prints exactly:

35

Test your understanding

QuestionSelect one

When should you use an exception instead of a return value to signal failure?

For every possible error

Only when you want to crash the program

When the failure is unusual or unexpected, and forcing every call site to check for it would be noisy

Whenever your method touches a file

QuestionSelect one

Why is a silent catch { } block dangerous?

It's slower than a specific catch

The compiler removes it

It hides bugs by swallowing every exception, even ones you never anticipated

It uses too much memory

On this page