用户工具

站点工具


csharp:第十章异常处理

第十章 异常处理

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>
    {
        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<T> ExecuteSafely<T>(Func<T> operation, string operationName)
    {
        try
        {
            var result = operation();
            return Result<T>.Success(result);
        }
        catch (BusinessException ex)
        {
            logger.LogWarning($"业务异常 [{operationName}]: {ex.ErrorCode} - {ex.Message}");
            return Result<T>.Failure(ex.Message, ex.ErrorCode);
        }
        catch (ArgumentException ex)
        {
            logger.LogWarning($"参数错误 [{operationName}]: {ex.Message}");
            return Result<T>.Failure(ex.Message, "ERR_INVALID_ARGUMENT");
        }
        catch (IOException ex)
        {
            logger.LogError($"IO错误 [{operationName}]: {ex.Message}");
            return Result<T>.Failure("系统繁忙,请稍后重试", "ERR_IO");
        }
        catch (Exception ex)
        {
            logger.LogCritical($"未预期错误 [{operationName}]: {ex}");
            return Result<T>.Failure("系统错误,请联系管理员", "ERR_INTERNAL");
        }
    }
 
    public async Task<Result<T>> ExecuteSafelyAsync<T>(Func<Task<T>> operation, string operationName)
    {
        try
        {
            var result = await operation();
            return Result<T>.Success(result);
        }
        catch (BusinessException ex)
        {
            logger.LogWarning($"业务异常 [{operationName}]: {ex.ErrorCode} - {ex.Message}");
            return Result<T>.Failure(ex.Message, ex.ErrorCode);
        }
        catch (Exception ex) when (ex is OperationCanceledException)
        {
            logger.LogInformation($"操作取消 [{operationName}]");
            return Result<T>.Failure("操作已取消", "ERR_CANCELED");
        }
        catch (Exception ex)
        {
            logger.LogCritical($"未预期错误 [{operationName}]: {ex}");
            return Result<T>.Failure("系统错误,请联系管理员", "ERR_INTERNAL");
        }
    }
}
 
// 结果模式
public class Result<T>
{
    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<T> Success(T value) => new Result<T>(true, value, null, null);
    public static Result<T> Failure(string message, string code) => 
        new Result<T>(false, default, message, code);
}
 
// 使用
public class OrderService
{
    private readonly ExceptionHandlingDemo exceptionHandler;
 
    public Result<Order> 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. 异常捕获顺序:分析以下代码的输出结果:

 <code csharp>
 try
 {
     throw new ArgumentNullException("test");
 }
 catch (ArgumentException ex)
 {
     Console.WriteLine("ArgumentException");
 }
 catch (NullReferenceException ex)
 {
     Console.WriteLine("NullReferenceException");
 }
 </code>

2. 自定义异常:创建ValidationException自定义异常,包含:

  1. 属性名
  2. 验证错误信息
  3. 多个验证错误的支持

3. 安全除法:编写SafeDivide方法,返回Result<double>,处理除以零的情况。

进阶练习

4. 重试机制:实现带重试的ExecuteWithRetry方法:

  1. 最多重试N次
  2. 支持指数退避
  3. 只重试特定类型的异常

5. 事务处理:实现一个简单的TransactionScope:

  1. 支持提交和回滚
  2. 使用异常处理回滚逻辑
  3. 支持嵌套事务

6. API异常中间件:为ASP.NET Core编写异常处理中间件:

  1. 统一返回格式
  2. 不同异常类型对应不同HTTP状态码
  3. 日志记录

挑战练习

7. 断路器模式:实现Circuit Breaker模式:

  1. Closed、Open、Half-Open三种状态
  2. 失败计数器
  3. 自动恢复尝试

8. 全局异常处理框架:设计完整的异常处理框架:

  1. 异常分类和映射
  2. 统一的错误响应
  3. 日志和监控集成
  4. 配置驱动的异常处理策略

本章小结

本章学习了C#异常处理:

  1. try-catch-finally结构
  2. 多个catch块的处理顺序
  3. 抛出和重新抛出异常
  4. 自定义异常的创建
  5. 异常处理最佳实践
  6. 异常过滤器
  7. 全局异常处理
  8. 异步异常处理
  9. 完整的异常处理系统

良好的异常处理是健壮应用程序的关键。

csharp/第十章异常处理.txt · 最后更改: 127.0.0.1