Exception Handling
## Learning Objectives
- Understand exception types
- Use try, catch, finally
- Throw custom exceptions
- Handle exceptions gracefully
## What is an Exception?
An event that disrupts normal program flow:
```csharp
class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // IndexOutOfRangeException!
}
}
```
## Exception Hierarchy
```text
Exception
├── SystemException
│ ├── ArgumentException
│ ├── IndexOutOfRangeException
│ ├── NullReferenceException
│ └── InvalidOperationException
├── IOException
│ ├── FileNotFoundException
│ └── DirectoryNotFoundException
├── FormatException
├── OverflowException
└── ... (many more)
```
## Try-Catch
### Basic Syntax
```csharp
try
{
int result = 10 / 0; // Might throw
}
catch (ArithmeticException ex)
{
Console.WriteLine("Cannot divide by zero!");
Console.WriteLine(ex.Message);
}
```
### Multiple Catch
```csharp
try
{
int[] arr = new int[5];
arr[10] = 100;
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Array index error!");
}
catch (ArithmeticException ex)
{
Console.WriteLine("Math error!");
}
catch (Exception ex)
{
Console.WriteLine("Generic error: " + ex.Message);
}
```
### Catch Order
Most specific first, then general:
```csharp
try
{
// code
}
catch (NullReferenceException ex) // Specific first
{
// handle
}
catch (RuntimeException ex) // Then more general
{
// handle
}
catch (Exception ex) // Finally, most general
{
// handle
}
```
## Finally Block
### Always Executes
```csharp
StreamReader? reader = null;
try
{
reader = new StreamReader("file.txt");
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine("File not found");
}
catch (IOException ex)
{
Console.WriteLine("IO Error: " + ex.Message);
}
finally
{
// Always executes, even if exception thrown
reader?.Dispose();
}
```
### Try-with-resources (C# 8+)
```csharp
// Compiler generates finally block automatically
try
{
using var reader = new StreamReader("file.txt");
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine("File not found");
}
// reader is automatically disposed
```
## Throwing Exceptions
### throw Keyword
```csharp
public static void ValidateAge(int age)
{
if (age < 0)
{
throw new ArgumentException("Age cannot be negative");
}
if (age > 150)
{
throw new ArgumentException("Age is too high");
}
}
```
### Common Exception Types
| Type | When to Throw |
|------|--------------|
| ArgumentException | Invalid argument |
| ArgumentNullException | null argument |
| InvalidOperationException | Invalid state |
| NotSupportedException | Operation not supported |
## Custom Exceptions
### Define Exception Class
```csharp
public class ValidationException : Exception
{
public ValidationException() { }
public ValidationException(string message) : base(message) { }
public ValidationException(string message, Exception inner)
: base(message, inner) { }
}
public class InvalidEmailException : ValidationException
{
public InvalidEmailException(string email)
: base($"Invalid email: {email}") { }
}
```
### Using Custom Exception
```csharp
public static void RegisterUser(string email)
{
if (!email.Contains("@"))
{
throw new InvalidEmailException(email);
}
// registration logic
}
// Handling
try
{
RegisterUser("invalid");
}
catch (InvalidEmailException ex)
{
Console.WriteLine(ex.Message); // Invalid email: invalid
}
```
## Exception Filters (C# 6+)
### When Clause
```csharp
try
{
// code that might throw
}
catch (Exception ex) when (ex is ArgumentException or FormatException)
{
Console.WriteLine("Invalid argument or format");
}
catch (Exception ex) when (ex.Message.Contains("specific"))
{
Console.WriteLine("Specific error occurred");
}
```
## Common Runtime Exceptions
```csharp
// NullReferenceException
string? str = null;
str.Length; // NRE
// IndexOutOfRangeException
int[] arr = { 1, 2 };
arr[5] = 10; // IOR
// DivideByZeroException
int x = 10 / 0; // DBZ
// FormatException
int.Parse("abc"); // FP
// InvalidCastException
object obj = "hello";
int num = (int)obj; // ICE
// OverflowException
checked
{
int i = int.MaxValue;
i++; // OE
}
```
## Exception Properties
```csharp
try
{
RiskyOperation();
}
catch (Exception ex)
{
ex.Message; // Brief description
ex.StackTrace; // Call stack
ex.InnerException; // Cause exception
ex.GetType(); // Exception type
}
```
## Best Practices
### Do
```csharp
// Catch specific exceptions
try
{
string content = File.ReadAllText("file.txt");
}
catch (FileNotFoundException)
{
// Handle specific exception
}
// Clean up with try-with-resources
try
{
using var connection = new DatabaseConnection();
// use connection
}
// Log and potentially rethrow
try
{
DoSomething();
}
catch (Exception ex)
{
Logger.Error("Failed", ex);
throw; // Rethrow original
}
```
### Don't
```csharp
// Don't swallow exceptions
try
{
DoSomething();
}
catch (Exception)
{
// Doing nothing hides bugs!
}
// Don't catch without action
try
{
DoSomething();
}
catch (Exception ex)
{
Console.WriteLine(ex); // At least log it
}
// Don't catch everything
try
{
DoSomething();
}
catch // Empty catch - bad!
{
}
```
## Rethrowing Exceptions
### throw vs throw
```csharp
catch (Exception ex)
{
Log(ex);
throw ex; // Resets stack trace to this location
}
catch (Exception ex)
{
Log(ex);
throw; // Preserves original stack trace (preferred)
}
```
## Summary
- Exceptions disrupt normal flow
- try-catch-finally handles exceptions
- finally always executes (or use try-with-resources)
- throw throws an exception
- Custom exceptions extend Exception
- Catch specific exceptions first
- Exception filters: `catch when (condition)`
- Never silently swallow exceptions
- Use `throw;` to rethrow preserved stack trace
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →