Exception Handling in C#
An exception is the .NET way of signalling that something went wrong while a program runs — a file is missing, input isn't a number, or a network call failed. If you ignore an exception, the program crashes. Exception handling lets you catch the problem, respond sensibly, and keep running. The core keywords are try (the risky code), catch (what to do when it fails), finally (cleanup that always runs), and throw (raise an exception yourself). Used well, they turn crashes into controlled, recoverable situations.
The basic try / catch
try
{
Console.Write("Enter a number: ");
int n = int.Parse(Console.ReadLine()!); // throws if the text isn't a number
Console.WriteLine($"Double is {n * 2}");
}
catch (FormatException)
{
// Runs only if int.Parse fails because the text wasn't a valid number.
Console.WriteLine("That wasn't a valid number.");
}
If the user types "hello", int.Parse throws a FormatException, control jumps to the catch block, and the program continues instead of crashing.
Catching specific exceptions
Catch the most specific type first. Each exception type carries useful information:
try
{
int[] scores = { 90, 80 };
Console.WriteLine(scores[5]); // index 5 doesn't exist
}
catch (IndexOutOfRangeException ex)
{
// 'ex' holds details about what happened.
Console.WriteLine($"Bad index: {ex.Message}");
}
catch (Exception ex)
{
// A general catch-all should come LAST, as a safety net.
Console.WriteLine($"Something else failed: {ex.Message}");
}
Exception is the base type of all exceptions, so a catch (Exception) matches everything. Always place it last; otherwise the specific catches become unreachable.
finally — cleanup that always runs
The finally block runs whether or not an exception happened — ideal for releasing resources:
try
{
Console.WriteLine("Opening connection...");
throw new InvalidOperationException("Server unavailable");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Handled: {ex.Message}");
}
finally
{
// Runs no matter what — success or failure.
Console.WriteLine("Closing connection.");
}
For things like files and streams, the using statement is even better — it disposes them automatically when the block ends, so you rarely need a manual finally for cleanup.
Throwing your own exceptions
Use throw to signal an error your code detects:
public decimal Withdraw(decimal amount, decimal balance)
{
if (amount <= 0)
throw new ArgumentException("Amount must be positive");
if (amount > balance)
throw new InvalidOperationException("Insufficient funds");
return balance - amount;
}
Pick a built-in type that fits the problem: ArgumentException for bad parameters, InvalidOperationException for an action that isn't valid right now.
Want to learn this properly?
Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.
Browse coursesCustom exceptions
For domain-specific errors, define your own type by inheriting from Exception:
public class CourseFullException : Exception
{
public CourseFullException(string message) : base(message) { }
}
// Later, in your enrollment logic:
// throw new CourseFullException("Batch already has 30 students");
A custom type lets callers catch (CourseFullException) and handle that one case precisely.
Prefer prevention over catching
Exceptions are for exceptional situations, not normal control flow. When you can validate cheaply, do so:
// Better than try/catch for parsing user input:
if (int.TryParse(Console.ReadLine(), out int value))
Console.WriteLine($"Got {value}");
else
Console.WriteLine("Please enter digits only.");
Common mistakes
- Swallowing exceptions silently. An empty
catch { }hides bugs. At minimum, log the error. - Catching
Exceptiontoo broadly and too early. Catch specific types first; reserve the general catch for a genuine safety net. - Using exceptions for normal flow. Don't rely on a thrown exception to handle expected input — use
TryParseand validation instead. - Forgetting cleanup. Use
usingfor disposable resources, orfinallyto ensure they're released even on failure. - Throwing
new Exception("..."). Choose a meaningful type so callers can handle it specifically.
FAQ
What's the difference between throw; and throw ex;? Plain throw; rethrows the current exception and preserves the original stack trace. throw ex; resets the stack trace, hiding where the error really started — avoid it.
Should every method use try/catch? No. Handle exceptions where you can do something meaningful about them, often at a higher level.
Keep learning
- Hub: Learn .NET
- Previous: Collections in C#
- Next: Data Access with ADO.NET
Write robust, real-world C# with a mentor in Jalgaon — join the waitlist for the Infoplanet .NET course at /courses/dotnet.
Want to learn this properly?
Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.
Browse coursesFounder, Infoplanet
Atul Kabra founded Infoplanet in 2001 and has spent over two decades teaching programming — C, C++, Java, databases and more — to students across Maharashtra.
Related guides
Data Access with ADO.NET
Connect to and query a database from C# with ADO.NET — connections, commands, parameterized queries, and data readers — written safely with using blocks.
Introduction to ASP.NET Core
Understand ASP.NET Core — the modern, cross-platform web framework for .NET — and build your first minimal web API with routing and JSON responses.
ASP.NET: Web Forms vs MVC vs Razor Pages
Understand the difference between ASP.NET Web Forms, MVC, and Razor Pages — including why Web Forms is legacy — to pick the right model on modern .NET.
