Exception handling in C# is a critical concept for writing robust and error-resilient applications. C# provides a structured and elegant way to handle errors using try, catch, finally, and throw keywords. In this comprehensive guide, we will cover how exception handling works, common exceptions, how to create custom exceptions, best practices, and more.
Exception handling is the process of responding to the occurrence of exceptionsβanomalous or exceptional conditions that disrupt the normal flow of a program's instructions. C# uses an object-oriented approach to exception handling, allowing developers to manage both system-generated and custom exceptions.
The most common form of exception handling is the try-catch block. The try block contains the code that may throw exceptions, and the catch block handles them.
try
{
int result = 10 / 0; // Will throw DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Cannot divide by zero.");
}
The finally block is used to execute code regardless of whether an exception was thrown or not. It's often used to release resources such as file handles or database connections.
try
{
int[] numbers = {1, 2, 3};
Console.WriteLine(numbers[5]); // Index out of range
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Index is out of range.");
}
finally
{
Console.WriteLine("This block is always executed.");
}
All exceptions in C# are derived from the base class System.Exception.
The throw keyword is used to manually throw an exception.
throw new InvalidOperationException("Operation not allowed.");
You can also re-throw an exception caught in a catch block using a bare throw.
try
{
// some code
}
catch (Exception ex)
{
Console.WriteLine("Logging the exception.");
throw; // rethrows the same exception
}
You can handle different types of exceptions using multiple catch blocks.
try
{
int[] arr = new int[3];
Console.WriteLine(arr[5]);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Index error: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("General error: " + ex.Message);
}
Exception filters allow you to handle exceptions conditionally using the when keyword.
try
{
int x = 10 / 0;
}
catch (DivideByZeroException ex) when (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
{
Console.WriteLine("Divide by zero on Monday.");
}
You can create your own exception classes by inheriting from Exception.
public class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message)
{
}
}
try
{
int age = 15;
if (age < 18)
throw new InvalidAgeException("Age must be 18 or above.");
}
catch (InvalidAgeException ex)
{
Console.WriteLine("Custom Exception: " + ex.Message);
}
An inner exception is used to capture an original exception when a new exception is thrown.
try
{
try
{
int.Parse("abc");
}
catch (FormatException ex)
{
throw new ApplicationException("Parsing failed", ex);
}
}
catch (ApplicationException ex)
{
Console.WriteLine("Outer Exception: " + ex.Message);
Console.WriteLine("Inner Exception: " + ex.InnerException.Message);
}
Nested try blocks can be used when you want to handle specific exceptions at different levels of logic.
try
{
try
{
string str = null;
Console.WriteLine(str.Length);
}
catch (NullReferenceException ex)
{
Console.WriteLine("Null reference handled.");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer block handled an exception.");
}
try
{
string content = File.ReadAllText("file.txt");
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
Console.WriteLine("File not found.");
}
catch (IOException ex)
{
Console.WriteLine("An I/O error occurred.");
}
string input = null;
try
{
Console.WriteLine(input.Length);
}
catch (NullReferenceException)
{
Console.WriteLine("Input is null.");
}try
{
Console.WriteLine("Enter age:");
int age = int.Parse(Console.ReadLine());
if (age < 0)
throw new ArgumentOutOfRangeException("Age cannot be negative.");
}
catch (FormatException)
{
Console.WriteLine("Please enter a valid number.");
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
}
public async Task LoadDataAsync()
{
try
{
string data = await File.ReadAllTextAsync("file.txt");
Console.WriteLine(data);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
Exceptions can propagate up the call stack if not handled immediately.
void Level1()
{
Level2();
}
void Level2()
{
Level3();
}
void Level3()
{
throw new Exception("Something went wrong.");
}
try
{
Level1();
}
catch (Exception ex)
{
Console.WriteLine("Caught at top level: " + ex.Message);
}
Exception handling in C# is a powerful mechanism to detect and manage runtime errors. Using try-catch-finally constructs properly can make your application more stable, readable, and professional. Remember to always handle only those exceptions you can deal with, log them appropriately, and avoid overusing general exceptions. Well-structured exception handling contributes to better software quality and maintainability.
By mastering exception handling in C#, youβll be prepared to build applications that can gracefully recover from unexpected situations and provide a better user experience.
Copyrights © 2024 letsupdateskills All rights reserved