Introduction to Error Handling in C#
In any application, especially in complex enterprise systems on the .NET platform, errors are inevitable. A user may enter incorrect data, a file may be unavailable, or a network connection may drop. Instead of letting the program crash, a skilled developer uses the exception handling mechanism.
In C#, the foundation of this mechanism is the try/catch/finally construct. However, the language's capabilities are not limited to this: there are the using and throw statements, as well as the ability to create custom exception types. In this article, we will thoroughly examine all key aspects of error handling in C# (.NET) to make your code not only reliable but also readable.
1. Basic Construct: try, catch, and finally
This is the foundation for getting started with exceptions. The try block contains code that could potentially cause an error. The catch block catches the exception and handles it. The finally block always executes, regardless of whether an error occurred or not — it is ideal for releasing resources (closing files, connections).
Syntax and Example
using System;using System.IO;
class Program{ static void Main() { try { Console.Write("Enter the file path: "); string path = Console.ReadLine(); string content = File.ReadAllText(path); Console.WriteLine("File contents:"); Console.WriteLine(content); } catch (FileNotFoundException ex) { Console.WriteLine($"Error: File not found. {ex.Message}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"Error: No access to the file. {ex.Message}"); } catch (Exception ex) // General catch for all other exceptions { Console.WriteLine($"Unknown error: {ex.Message}"); } finally { Console.WriteLine("Finally block executed. Releasing resources (if needed)."); } }}Important points:
- The order of catch blocks matters: you should first catch more specific exceptions (e.g.,
FileNotFoundException), then more general ones (Exception). If you place the general block first, the more specific blocks will never be reached. - The finally block is optional. If you don't need to guarantee resource release, you can omit it.
- Don't catch
Exceptionunnecessarily. If you don't know how to handle an error, it's better to let the program crash — this will help identify the problem during development.
2. Resource Release with using
Although finally is a powerful tool, writing it every time for things like closing files or database connections is tedious. For objects that implement the IDisposable interface, C# has the using statement. It guarantees that the Dispose() method is called when exiting the block, even if an exception occurs.
Comparison of Approaches
Without using (manual release):
StreamReader reader = null;try{ reader = new StreamReader("data.txt"); string line = reader.ReadLine(); // ... work with reader}finally{ if (reader != null) reader.Dispose();}Using the using statement:
using (StreamReader reader = new StreamReader("data.txt")){ string line = reader.ReadLine(); // ... work with reader}// Dispose() will be called automatically