Exception handling is a fundamental part of writing robust and reliable C# applications. This tutorial will cover the basics of exception handling, various techniques, best practices, and advanced concepts.
Exception handling in C# is used to manage errors and unexpected events in a controlled manner. This ensures that your application can handle runtime errors gracefully and continue to operate or fail gracefully.
The basic try-catch block is used to catch and handle exceptions. Here's an example:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
int result = 10 / 0; // This will cause a DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
You can catch specific exceptions to handle different types of errors appropriately. Here's how:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // This will cause an IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Index out of range: {ex.Message}");
}
}
}
The finally block is used to execute code regardless of whether an exception was thrown or not. Here's an example:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
int result = 10 / 0; // This will cause a DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
finally
{
Console.WriteLine("This will always be executed.");
}
}
}
You can throw exceptions using the throw keyword. This is useful for indicating errors in your methods. Here's how:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
ValidateAge(-1);
}
catch (ArgumentException ex)
{
Console.WriteLine($"Validation error: {ex.Message}");
}
}
public static void ValidateAge(int age)
{
if (age < 0)
{
throw new ArgumentException("Age cannot be negative");
}
}
}
Custom exceptions allow you to define your own exception types. This is useful for domain-specific error handling. Here's an example:
using System;
namespace ExceptionHandlingExamples;
public class InvalidOrderException : Exception
{
public InvalidOrderException(string message) : base(message) { }
}
public class Program
{
public static void Main(string[] args)
{
try
{
ProcessOrder(-1);
}
catch (InvalidOrderException ex)
{
Console.WriteLine($"Order processing error: {ex.Message}");
}
}
public static void ProcessOrder(int orderId)
{
if (orderId <= 0)
{
throw new InvalidOrderException("Order ID must be greater than zero");
}
}
}
Exceptions in C# have several useful properties, such as Message, StackTrace, and InnerException. Here's how to use them:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
int result = 10 / 0; // This will cause a DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Exception Message: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
}
}
}
Inner exceptions are used to capture the original exception in a chain of exceptions. Here's how to handle them:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
CauseInnerException();
}
catch (Exception ex)
{
Console.WriteLine($"Outer Exception: {ex.Message}");
if (ex.InnerException != null)
{
Console.WriteLine($"Inner Exception: {ex.InnerException.Message}");
}
}
}
public static void CauseInnerException()
{
try
{
int result = 10 / 0; // This will cause a DivideByZeroException
}
catch (DivideByZeroException ex)
{
throw new InvalidOperationException("An error occurred in the inner method", ex);
}
}
}
Global exception handling is used to catch and handle exceptions at the application level. Here's an example:
using System;
using System.Threading.Tasks;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
Console.WriteLine($"Unobserved Task Exception: {eventArgs.Exception.Message}");
eventArgs.SetObserved();
};
throw new Exception("Global exception test");
}
static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine($"Unhandled exception: {((Exception)e.ExceptionObject).Message}");
}
}
Exception filters provide a way to execute code conditionally based on the type or properties of an exception. Here's how to use them:
using System;
namespace ExceptionHandlingExamples;
public class Program
{
public static void Main(string[] args)
{
try
{
int result = 10 / 0; // This will cause a DivideByZeroException
}
catch (DivideByZeroException ex) when (ex.Message.Contains("zero"))
{
Console.WriteLine("Caught a divide by zero exception.");
}
}
}
Here are some best practices for handling exceptions in C#:
Advanced exception handling techniques include custom logging, wrapping exceptions, and using patterns like retry logic. Here's an example:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ExceptionHandlingExamples;
public class Program
{
public static async Task Main(string[] args)
{
try
{
await RetryPolicy.ExecuteAsync(async () =>
{
var client = new HttpClient();
var response = await client.GetAsync("https://nonexistent.url");
response.EnsureSuccessStatusCode();
});
}
catch (Exception ex)
{
Console.WriteLine($"Final failure: {ex.Message}");
}
}
}
public static class RetryPolicy
{
public static async Task ExecuteAsync(Func<Task> action, int retryCount = 3)
{
for (int i = 0; i < retryCount; i++)
{
try
{
await action();
return;
}
catch
{
if (i == retryCount - 1)
{
throw;
}
}
}
}
}
Exception handling is a critical aspect of writing robust C# applications. By understanding and applying best practices, you can handle errors gracefully and maintain the stability of your application.