====== 第十章 异常处理 ======
===== 10.1 异常基础 =====
=== 什么是异常 ===
异常是程序执行过程中发生的错误或异常情况,它中断了正常的程序流程。C#使用异常机制来处理错误情况。
=== 异常类层次结构 ===
System.Object
System.Exception
System.SystemException
System.ArithmeticException
System.DivideByZeroException
System.OverflowException
System.ArgumentException
System.ArgumentNullException
System.ArgumentOutOfRangeException
System.IndexOutOfRangeException
System.InvalidOperationException
System.NullReferenceException
System.StackOverflowException
System.ApplicationException
===== 10.2 try-catch-finally =====
=== 基本结构 ===
try
{
// 可能抛出异常的代码
int result = 10 / 0; // 会抛出DivideByZeroException
}
catch (DivideByZeroException ex)
{
// 处理特定异常
Console.WriteLine($"除零错误: {ex.Message}");
}
catch (Exception ex)
{
// 处理所有其他异常
Console.WriteLine($"错误: {ex.Message}");
}
finally
{
// 无论是否发生异常都会执行
Console.WriteLine("清理资源");
}
=== 多个catch块 ===
public void ProcessFile(string path)
{
try
{
string content = File.ReadAllText(path);
int number = int.Parse(content);
Console.WriteLine(number * 2);
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"文件未找到: {ex.FileName}");
}
catch (FormatException ex)
{
Console.WriteLine("文件内容格式错误");
}
catch (IOException ex)
{
Console.WriteLine($"IO错误: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"未知错误: {ex.Message}");
}
}
=== finally块 ===
public void ReadFile(string path)
{
StreamReader reader = null;
try
{
reader = new StreamReader(path);
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine("文件不存在");
}
finally
{
// 确保资源被释放
reader?.Close();
Console.WriteLine("文件读取完成");
}
}
// 更好的方式 - using语句
public void ReadFileBetter(string path)
{
try
{
using (var reader = new StreamReader(path))
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
} // 自动调用Dispose
}
catch (FileNotFoundException)
{
Console.WriteLine("文件不存在");
}
}
===== 10.3 抛出异常 =====
=== throw语句 ===
public void Withdraw(decimal amount)
{
if (amount < 0)
{
throw new ArgumentException("取款金额不能为负数", nameof(amount));
}
if (amount > Balance)
{
throw new InvalidOperationException("余额不足");
}
Balance -= amount;
}
=== 重新抛出异常 ===
try
{
ProcessData();
}
catch (Exception ex)
{
// 方式1:保留原始堆栈信息
throw;
// 方式2:丢失原始堆栈
// throw ex;
// 方式3:包装异常
// throw new ApplicationException("处理数据失败", ex);
}
===== 10.4 自定义异常 =====
=== 创建自定义异常 ===
// 业务异常基类
public class BusinessException : Exception
{
public string ErrorCode { get; }
public DateTime Timestamp { get; }
public BusinessException(string message, string errorCode)
: base(message)
{
ErrorCode = errorCode;
Timestamp = DateTime.Now;
}
public BusinessException(string message, string errorCode, Exception innerException)
: base(message, innerException)
{
ErrorCode = errorCode;
Timestamp = DateTime.Now;
}
}
// 具体的业务异常
public class InsufficientFundsException : BusinessException
{
public decimal RequestedAmount { get; }
public decimal AvailableBalance { get; }
public InsufficientFundsException(decimal requested, decimal available)
: base($"余额不足。请求: {requested:C}, 可用: {available:C}", "ERR_INSUFFICIENT_FUNDS")
{
RequestedAmount = requested;
AvailableBalance = available;
}
}
public class InvalidTransferException : BusinessException
{
public string AccountNumber { get; }
public InvalidTransferException(string accountNumber, string reason)
: base($"转账失败: {reason}", "ERR_INVALID_TRANSFER")
{
AccountNumber = accountNumber;
}
}
===== 10.5 异常处理最佳实践 =====
=== DO's ===
// 1. 捕获特定的异常
public void GoodPractice1()
{
try
{
int.Parse("abc");
}
catch (FormatException ex) // 好的做法
{
Console.WriteLine("格式错误");
}
}
// 2. 使用finally或using清理资源
public void GoodPractice2()
{
using var stream = new FileStream("data.txt", FileMode.Open);
// 处理文件
}
// 3. 提供有意义的错误信息
public void GoodPractice3(string userName)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentException("用户名不能为空或空白", nameof(userName));
}
}
// 4. 创建自定义异常
public void GoodPractice4()
{
throw new InsufficientFundsException(100, 50);
}
=== DON'Ts ===
// 1. 不要捕获所有异常而不处理
public void BadPractice1()
{
try
{
DoWork();
}
catch // 不好的做法 - 吞掉所有异常
{
// 空的catch块
}
}
// 2. 不要使用异常控制流程
public void BadPractice2(string input)
{
try
{
int value = int.Parse(input); // 不好的做法
}
catch
{
value = 0;
}
}
// 好的做法
public void GoodAlternative(string input)
{
if (int.TryParse(input, out int value))
{
// 使用value
}
else
{
value = 0;
}
}
// 3. 不要抛出System.Exception
public void BadPractice3()
{
throw new Exception("错误"); // 不好的做法
}
// 好的做法
public void GoodPracticeAlternative()
{
throw new InvalidOperationException("操作无效");
}
===== 10.6 异常过滤器(C# 6+) =====
try
{
ProcessData();
}
catch (HttpException ex) when (ex.StatusCode == 404)
{
Console.WriteLine("资源未找到");
}
catch (HttpException ex) when (ex.StatusCode >= 500)
{
Console.WriteLine("服务器错误");
}
catch (HttpException ex)
{
Console.WriteLine($"HTTP错误: {ex.StatusCode}");
}
// 使用日志记录的异常过滤器
private static bool LogException(Exception ex)
{
Console.WriteLine($"[LOG] {ex.Message}");
return false; // 返回false让异常继续传播
}
try
{
RiskyOperation();
}
catch (Exception ex) when (LogException(ex))
{
// 不会执行到这里,因为过滤器返回false
}
===== 10.7 全局异常处理 =====
=== 控制台应用程序 ===
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
try
{
RunApplication();
}
catch (Exception ex)
{
Console.WriteLine($"应用程序错误: {ex.Message}");
}
}
static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = e.ExceptionObject as Exception;
Console.WriteLine($"未处理异常: {ex?.Message}");
// 记录日志、发送通知等
}
static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
Console.WriteLine($"未观察的任务异常: {e.Exception.Message}");
e.SetObserved(); // 标记为已观察,防止进程终止
}
}
===== 10.8 异步异常处理 =====
public async Task ProcessAsync()
{
try
{
await Task.Delay(1000);
throw new InvalidOperationException("异步操作失败");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"异步错误: {ex.Message}");
throw; // 重新抛出
}
}
// 多个异步任务
public async Task ProcessMultipleAsync()
{
var tasks = new List
{
Task.Run(() => throw new InvalidOperationException("任务1失败")),
Task.Run(() => throw new ArgumentException("任务2失败")),
Task.Run(() => { })
};
try
{
await Task.WhenAll(tasks);
}
catch
{
foreach (var task in tasks)
{
if (task.IsFaulted)
{
Console.WriteLine($"任务错误: {task.Exception?.InnerException?.Message}");
}
}
}
}
// 使用AggregateException
public void HandleAggregateException()
{
try
{
Parallel.For(0, 10, i =>
{
if (i == 5)
throw new InvalidOperationException($"索引 {i} 错误");
});
}
catch (AggregateException ex)
{
foreach (var inner in ex.InnerExceptions)
{
Console.WriteLine($"内部错误: {inner.Message}");
}
}
}
===== 10.9 实战:完整的异常处理系统 =====
public class ExceptionHandlingDemo
{
private readonly ILogger logger;
public ExceptionHandlingDemo(ILogger logger)
{
this.logger = logger;
}
public Result ExecuteSafely(Func operation, string operationName)
{
try
{
var result = operation();
return Result.Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning($"业务异常 [{operationName}]: {ex.ErrorCode} - {ex.Message}");
return Result.Failure(ex.Message, ex.ErrorCode);
}
catch (ArgumentException ex)
{
logger.LogWarning($"参数错误 [{operationName}]: {ex.Message}");
return Result.Failure(ex.Message, "ERR_INVALID_ARGUMENT");
}
catch (IOException ex)
{
logger.LogError($"IO错误 [{operationName}]: {ex.Message}");
return Result.Failure("系统繁忙,请稍后重试", "ERR_IO");
}
catch (Exception ex)
{
logger.LogCritical($"未预期错误 [{operationName}]: {ex}");
return Result.Failure("系统错误,请联系管理员", "ERR_INTERNAL");
}
}
public async Task> ExecuteSafelyAsync(Func> operation, string operationName)
{
try
{
var result = await operation();
return Result.Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning($"业务异常 [{operationName}]: {ex.ErrorCode} - {ex.Message}");
return Result.Failure(ex.Message, ex.ErrorCode);
}
catch (Exception ex) when (ex is OperationCanceledException)
{
logger.LogInformation($"操作取消 [{operationName}]");
return Result.Failure("操作已取消", "ERR_CANCELED");
}
catch (Exception ex)
{
logger.LogCritical($"未预期错误 [{operationName}]: {ex}");
return Result.Failure("系统错误,请联系管理员", "ERR_INTERNAL");
}
}
}
// 结果模式
public class Result
{
public bool IsSuccess { get; }
public T Value { get; }
public string ErrorMessage { get; }
public string ErrorCode { get; }
private Result(bool isSuccess, T value, string errorMessage, string errorCode)
{
IsSuccess = isSuccess;
Value = value;
ErrorMessage = errorMessage;
ErrorCode = errorCode;
}
public static Result Success(T value) => new Result(true, value, null, null);
public static Result Failure(string message, string code) =>
new Result(false, default, message, code);
}
// 使用
public class OrderService
{
private readonly ExceptionHandlingDemo exceptionHandler;
public Result PlaceOrder(OrderRequest request)
{
return exceptionHandler.ExecuteSafely(() =>
{
ValidateRequest(request);
var order = CreateOrder(request);
SaveOrder(order);
return order;
}, "PlaceOrder");
}
private void ValidateRequest(OrderRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Items == null || !request.Items.Any())
throw new BusinessException("订单不能为空", "ERR_EMPTY_ORDER");
}
private Order CreateOrder(OrderRequest request) => new Order();
private void SaveOrder(Order order) { }
}
===== 练习题 =====
=== 基础练习 ===
1. **异常捕获顺序**:分析以下代码的输出结果:
try
{
throw new ArgumentNullException("test");
}
catch (ArgumentException ex)
{
Console.WriteLine("ArgumentException");
}
catch (NullReferenceException ex)
{
Console.WriteLine("NullReferenceException");
}
2. **自定义异常**:创建ValidationException自定义异常,包含:
- 属性名
- 验证错误信息
- 多个验证错误的支持
3. **安全除法**:编写SafeDivide方法,返回Result,处理除以零的情况。
=== 进阶练习 ===
4. **重试机制**:实现带重试的ExecuteWithRetry方法:
- 最多重试N次
- 支持指数退避
- 只重试特定类型的异常
5. **事务处理**:实现一个简单的TransactionScope:
- 支持提交和回滚
- 使用异常处理回滚逻辑
- 支持嵌套事务
6. **API异常中间件**:为ASP.NET Core编写异常处理中间件:
- 统一返回格式
- 不同异常类型对应不同HTTP状态码
- 日志记录
=== 挑战练习 ===
7. **断路器模式**:实现Circuit Breaker模式:
- Closed、Open、Half-Open三种状态
- 失败计数器
- 自动恢复尝试
8. **全局异常处理框架**:设计完整的异常处理框架:
- 异常分类和映射
- 统一的错误响应
- 日志和监控集成
- 配置驱动的异常处理策略
===== 本章小结 =====
本章学习了C#异常处理:
- try-catch-finally结构
- 多个catch块的处理顺序
- 抛出和重新抛出异常
- 自定义异常的创建
- 异常处理最佳实践
- 异常过滤器
- 全局异常处理
- 异步异常处理
- 完整的异常处理系统
良好的异常处理是健壮应用程序的关键。