Understanding Exceptions in C#
Exceptions are a critical part of robust C# programming. They provide a structured way to handle runtime errors, allowing your application to gracefully recover from unexpected situations.
What are Exceptions?
An exception is an event that occurs during the execution of a program that disrupts the normal flow of the program's instructions. When an exception occurs, an object is created that describes the error that occurred. This object is then "thrown" and the system attempts to "catch" it.
The .NET Framework provides a rich hierarchy of exception classes, all derived from the System.Exception
class.
Common Exception Types
System.NullReferenceException
: Thrown when a null reference is used.System.IndexOutOfRangeException
: Thrown when an array index is outside the array's bounds.System.ArgumentException
: Thrown when a method receives an argument that is invalid.System.DivideByZeroException
: Thrown when an attempt is made to divide an integer by zero.System.IO.FileNotFoundException
: Thrown when a file that is expected to be found is not found.
The `try-catch-finally` Block
The primary mechanism for handling exceptions in C# is the try-catch-finally
block:
try
{
// Code that might throw an exception
int result = Divide(10, 0);
Console.WriteLine($"Result: {result}");
}
catch (DivideByZeroException ex)
{
// Handle the DivideByZeroException
Console.WriteLine($"Error: Cannot divide by zero. {ex.Message}");
}
catch (Exception ex)
{
// Handle any other type of exception
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}
finally
{
// Code that will always execute, whether an exception occurred or not
Console.WriteLine("Cleanup operations.");
}
int Divide(int numerator, int denominator)
{
return numerator / denominator;
}
Throwing Exceptions
You can also throw exceptions manually using the throw
keyword:
public void ValidateInput(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException(nameof(input), "Input cannot be null or empty.");
}
// ... further validation
}
Custom Exceptions
For domain-specific error conditions, it's good practice to create custom exception classes by deriving from System.Exception
:
public class InvalidUserDataException : Exception
{
public InvalidUserDataException(string message) : base(message) { }
public InvalidUserDataException(string message, Exception innerException) : base(message, innerException) { }
}
Best Practices
- Catch specific exceptions rather than a general
Exception
whenever possible. - Do not swallow exceptions; log them or re-throw them if you cannot handle them locally.
- Use
try-catch-finally
for operations that might fail. - Throw exceptions for error conditions that violate a method's contract.
- Provide meaningful exception messages.
try { /* do something */ } catch { /* do nothing */ }
This hides potential problems.