Exception Handling in .NET Core
Exception handling is a fundamental aspect of robust application development. It allows you to gracefully manage runtime errors that occur during the execution of your .NET Core applications, preventing unexpected crashes and providing a better user experience.
What are Exceptions?
An exception is an event that occurs during program execution that disrupts the normal flow of instructions. When an exception occurs, the program's execution is suspended and the system searches for code that can handle the exception. If no handler is found, the program terminates.
The Exception Class Hierarchy
.NET Core exceptions are represented by classes that derive from the System.Exception
base class. Key exception classes include:
System.ArgumentException
: Indicates that an argument passed to a method is invalid.System.NullReferenceException
: Occurs when a variable that is supposed to refer to an object is set tonull
.System.IndexOutOfRangeException
: Occurs when an array index is outside the valid range.System.InvalidOperationException
: Indicates that a method call is invalid for the object's current state.System.IO.IOException
: Represents errors related to input/output operations.System.NotImplementedException
: Used to indicate that a requested feature or method has not yet been implemented.
Throwing Exceptions
You can throw exceptions using the throw
keyword when you detect an error condition that your code cannot handle.
Example: Throwing a ArgumentOutOfRangeException
public void SetAge(int age)
{
if (age < 0 || age > 120)
{
throw new ArgumentOutOfRangeException(nameof(age), "Age must be between 0 and 120.");
}
this.age = age;
}
Handling Exceptions: try-catch-finally
The try-catch-finally
block is the primary mechanism for handling exceptions:
try
: The code that might throw an exception is placed within thetry
block.catch
: If an exception occurs in thetry
block, the correspondingcatch
block is executed. You can have multiplecatch
blocks to handle different types of exceptions.finally
: The code in thefinally
block is always executed, regardless of whether an exception occurred or was caught. This is ideal for releasing resources.
Example: Using try-catch-finally
try
{
// Code that might throw an exception
string filePath = "data.txt";
string content = File.ReadAllText(filePath);
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"Error: The file was not found. {ex.Message}");
}
catch (IOException ex)
{
Console.WriteLine($"An I/O error occurred. {ex.Message}");
}
catch (Exception ex) // Catch-all for any other exceptions
{
Console.WriteLine($"An unexpected error occurred. {ex.Message}");
}
finally
{
// Code that always runs, e.g., closing a file or releasing a connection
Console.WriteLine("Cleanup operations performed.");
}
Exception Filters
Exception filters allow you to specify conditions for when a catch
block should be executed. This can be useful for logging or conditional handling without "catching" the exception definitively.
Example: Using an Exception Filter
try
{
// ... code that might throw ...
}
catch (Exception ex) when (ex.Message.Contains("specific error"))
{
Console.WriteLine($"Handling a specific error: {ex.Message}");
}
Best Practices for Exception Handling
- Be specific: Catch specific exception types rather than a general
Exception
when possible. - Don't suppress exceptions: If you catch an exception, either handle it meaningfully or re-throw it (possibly wrapping it in a new exception).
- Use
finally
for cleanup: Ensure resources are released properly. - Provide informative messages: Exception messages should be helpful for debugging.
- Avoid using exceptions for control flow: Exceptions are for exceptional circumstances, not for normal program logic.
- Log exceptions: Implement robust logging to track and diagnose errors.