Deep explanation — Exception classes &
handling in C# (step-by-step, with fixes,
best practice & exam tips)
Nice — you’ve pasted the core material. Below I’ll explain every important word and
concept deeply, fix the broken/unsafe examples, show idiomatic code, and give practical
prevention/handling advice you should memorize for exams and real projects.
1) Quick summary (one sentence)
All C# exceptions are objects (instances of classes) that inherit from System.Exception. The
CLR and framework throw many built-in exception types (system exceptions); you can also
create your own by subclassing Exception. Use try / catch / finally to handle exceptions,
throw to raise them, and prefer specific handling, not blanket catches.
2) The class hierarchy — important words explained
System.Exception
The root base class for all exceptions. Every exception object has properties such as
.Message, .StackTrace, .InnerException, and .Data. When you catch Exception
you catch everything derived from it.
System.SystemException
A base class used by the runtime for predefined system exceptions (e.g.,
NullReferenceException, IndexOutOfRangeException). You generally should
not derive your application exceptions from SystemException or catch
SystemException specifically — it’s meant to identify CLR-thrown errors.
System.ApplicationException
Historically intended as a base for user-defined exceptions. Do not use it as the base
for your custom exceptions — Microsoft recommends deriving directly from
Exception. ApplicationException provides no distinct runtime behavior and is
rarely useful.
User-defined exception
A class you write to represent domain-specific errors. Name it SomethingException,
derive from Exception, provide standard constructors (message, message+inner),
optionally [Serializable] and the protected serialization ctor if you target .NET
Framework serialization.
3) Important built-in exception types — what they mean, when
they’re thrown, and how to prevent/handle them
I’ll cover the types you listed plus a few commonly encountered others. For each: what,
when, how to avoid, how to handle.
1. System.IO.IOException
o What: General I/O problem (file locks, disk errors). Specific subclasses
include FileNotFoundException, DirectoryNotFoundException,
PathTooLongException, etc.
o When: File.Open(...), StreamReader, network file ops.
o Prevent: Validate paths, protect against race conditions, check disk space,
handle permissions. But prefer try/catch — checking File.Exists then
opening still has race conditions.
o Handle: Catch the specific subclass you expect (e.g.,
FileNotFoundException) or IOException for general I/O; log and either
retry or show user-friendly message.
2. System.IndexOutOfRangeException
o What: Accessing array/list outside bounds (arr[10] when length ≤10).
o Prevent: Always validate indices (i >= 0 && i < arr.Length) or use
foreach/LINQ which avoids direct index access.
o Handle: Prefer prevention. If it can happen due to external data, validate
inputs early.
3. System.ArrayTypeMismatchException
o What: Occurs with array covariance when you assign an incompatible
element into a reference-type array (e.g., object[] arr = new string[5];
arr[0] = 123;).
o Prevent: Avoid array covariance pitfalls — prefer generic collections
(List<T>) which are type-safe.
o Handle: Fix the design so types line up; don’t catch to mask the error.
4. System.NullReferenceException
o What: Attempt to use a reference that is null (e.g., obj.Prop when obj ==
null).
o Prevent: Null-checks, defensive programming, C# 8+ nullable reference types
(string? vs string) and the ?. operator, ?? operator to provide defaults.
o Handle: Rarely recoverable at the point of error — better to prevent and
validate inputs. Catching NullReferenceException globally is poor practice.
5. System.DivideByZeroException
o What: Integer division by zero. (int x = 10 / 0;)
o Note: Floating-point division does not throw — it yields Infinity or NaN.
o Prevent: Validate divisor before dividing.
o Handle: Catch or validate; provide error/fallback.
6. System.InvalidCastException
o What: Bad explicit cast (e.g., (StreamReader)someObject when not a
StreamReader).
o Prevent: Use is or as with null-check: if (obj is StreamReader sr)
{ ... } or var sr = obj as StreamReader; if (sr != null) ...
o Handle: Use checks before casting instead of catching.
7. System.OutOfMemoryException
o What: Not enough memory to fulfill allocation (e.g., new byte[huge]).
o Prevent: Avoid very large allocations, stream processing, chunking data, use
IEnumerable streaming, avoid caching everything.
o Handle: Often unrecoverable; try to fail gracefully, release resources, log, and
restart if necessary.
8. System.StackOverflowException
o What: Caused by unbounded recursion or excessively deep call chain.
o Important: Starting with .NET 2.0+, StackOverflowException typically
cannot be caught — the process will be terminated. So prevention is critical.
o Prevent: Ensure recursive calls have a termination condition, convert
recursion into iteration when needed.
Other useful exceptions:
FormatException — invalid string format for parsing (int.Parse("abc")). Use
TryParse.
ArgumentNullException / ArgumentException — invalid method arguments; throw
these in your public API to indicate misuse.
OverflowException — arithmetic overflow in a checked context.
AggregateException — used by TPL when multiple exceptions occur concurrently;
inspect .InnerExceptions.
4) Handling exceptions — core patterns & corrected examples
Correct pattern for try/catch/finally (DivNumbers example, corrected)
using System;
class DivNumbers
{
public void Division(int num1, int num2)
{
int result = 0;
try
{
result = num1 / num2; // may throw DivideByZeroException
}
catch (DivideByZeroException e)
{
Console.WriteLine("Exception caught: {0}", e.Message);
// optionally: log e.ToString() for diagnostics
}
finally
{
Console.WriteLine("Result: {0}", result);
}
}
static void Main()
{
var d = new DivNumbers();
d.Division(25, 0);
}