C# 错误处理:try/catch 及 .NET 高级技术

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C# 高级错误处理:从基础 try/catch 到工业级模式

\n\n

错误处理不仅仅是捕获异常,更是设计可靠系统的艺术。在 C# (.NET) 中,开发者常常局限于 try/catch 结构,但该平台提供了更强大的机制。本文将深入探讨高级技术:从异常过滤器到重试模式以及错误上下文传递。

\n\n

1. 异常过滤器:精确而非占位

\n\n

C# 6.0 引入的异常过滤器 (Exception Filters) 允许在进入 catch 块时指定条件,而无需重新抛出异常。这比在块内使用 if 的经典方法要清晰得多。

\n\n
try\n{\n    // 可能抛出异常的代码\n    var result = ProcessData(input);\n}\ncatch (CustomException ex) when (ex.ErrorCode == 1001)\n{\n    // 仅处理错误代码为 1001 的错误\n    Log.Error($"严重错误:{ex.Message}");\n    throw; // 如果需要,继续向上抛出\n}\ncatch (CustomException ex) when (ex.IsRecoverable)\n{\n    // 可恢复的错误 — 记录日志并继续\n    Log.Warning($"可恢复错误:{ex.Message}");\n    RetryOperation();\n}
\n\n

优点:

\n
    \n
  • 无需为了过滤而再次 throw。
  • \n
  • 提高可读性和性能(调用栈不会被破坏)。
  • \n
  • 可以组合多个具有不同条件的 catch 块。
  • \n
\n\n

2. 重试模式 (Retry) 与 Polly

\n\n

并非所有错误都是致命的。对于临时故障(网络问题、数据库锁定),请使用重试模式。C# 内置功能有限,因此推荐使用 Polly 库。

\n\n
using Polly;\nusing Polly.Retry;\n\n// 定义重试策略\nAsyncRetryPolicy retryPolicy = Policy\n    .Handle<HttpRequestException>() // 仅处理 HTTP 错误\n    .WaitAndRetryAsync(\n        3, // 重试次数\n        retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指数退避延迟\n        onRetry: (exception, timeSpan, retryCount, context) =>\n        {\n            Log.Warning($"第 {retryCount} 次尝试失败。{timeSpan.Seconds} 秒后重试。");\n        });\n\n// 应用策略\nawait retryPolicy.ExecuteAsync(async () =>\n{\n    var response = await httpClient.GetAsync("https://api.example.com/data");\n    response.EnsureSuccessStatusCode();\n});
\n\n

重要提示:不要使用无限重试。始终设置最大尝试次数,并使用 Circuit Breaker(断路器)来保护系统免受级联故障的影响。

\n\n

3. 正确传递异常:throw vs throw ex

\n\n

最常见的错误之一是丢失调用栈。对于调试而言,throw 和 throw ex 之间的区别至关重要。

\n\n
try\n{\n    // ...\n}\ncatch (Exception ex)\n{\n    // 错误:丢失原始调用栈\n    // throw ex;  \n\n    // 正确:保留调用栈\n    throw;\n\n    // 或者:添加上下文,同时不丢失原始信息\n    // throw new ApplicationException("模块 X 中发生错误", ex);\n}
\n\n

如果需要添加上下文,请使用 ExceptionDispatchInfo(来自 System.Runtime.ExceptionServices 命名空间)以在线程间重新抛出时精确保留调用栈。

\n\n
ExceptionDispatchInfo.Capture(exception).Throw(); // 保留原始调用栈
\n\n

4. 自定义异常与错误上下文

\n\n

标准异常通常不包含业务上下文。创建带有额外属性的自定义异常类。

\n\n
public class PaymentProcessingException : Exception\n{\n    public int OrderId { get; }\n    public dec                

博客

书籍推荐