How Try-Catch-Finally Works in C
In C#, robust error handling relies on try,
catch, and finally blocks to detect runtime
errors, recover from unexpected failures, and safely release critical
system resources. This article provides a straightforward breakdown of
how each block operates individually, how they work together to control
program execution during an error, and the core rules for implementing
them effectively in your C# applications.
The try Block
The try block contains the code that could potentially
throw an exception during execution. When code inside a try
block executes normally without errors, program flow moves directly past
any associated catch blocks and enters the
finally block (if present). If an error occurs, execution
inside the try block halts immediately at the line that
caused the error, and the runtime searches for a matching
catch block.
try
{
int divisor = 0;
int result = 10 / divisor; // Throws DivideByZeroException
}The catch Block
A catch block defines the error-handling logic when a
specific exception occurs within the try block. You can
define multiple catch blocks to handle different types of
exceptions differently. C# evaluates catch blocks from top
to bottom, so more specific exceptions must appear before general
exceptions like System.Exception.
C# also supports exception filters using the when
keyword, allowing you to catch exceptions only under certain runtime
conditions:
try
{
string content = File.ReadAllText("data.txt");
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"File missing: {ex.Message}");
}
catch (IOException ex) when (ex.HResult == -2147024864)
{
Console.WriteLine("File is locked by another process.");
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}If no matching catch block is found, the exception
bubbles up the call stack to the calling method. If left unhandled, the
application terminates.
The finally Block
The finally block contains code that always runs,
regardless of whether an exception was thrown, caught, or completely
unhandled. It executes even if the try or
catch block contains a return,
break, or continue statement.
Because of this guaranteed execution, finally blocks are
primarily used for resource cleanup, such as closing file handles,
database connections, or network sockets.
FileStream file = null;
try
{
file = File.Open("log.txt", FileMode.Open);
// Perform operations on the file
}
catch (IOException ex)
{
Console.WriteLine($"IO Error: {ex.Message}");
}
finally
{
if (file != null)
{
file.Close();
Console.WriteLine("File stream successfully closed.");
}
}Note: For types that implement IDisposable, the C#
using statement or using declaration provides
a cleaner shorthand that translates into a try-finally
block under the hood.
Execution Order Summary
trybegins: Code runs line-by-line until completion or an exception is thrown.- Exception occurs: Execution immediately jumps to
the first matching
catchblock. catchexecutes: The error is handled, logged, or re-thrown using thethrow;statement.finallyexecutes: Cleanup code runs regardless of whether an exception occurred or was handled.- Program continues: Normal execution resumes after
the entire structure, unless an unhandled exception or an explicit
throwterminates the thread.