异常处理是软件开发中保障程序健壮性的核心机制之一。在C#中,异常处理通过结构化的方式帮助开发者优雅地处理运行时错误,避免程序崩溃,同时提供清晰的调试信息。本文将深入探讨C#异常处理的完整体系,涵盖语法结构、最佳实践、高级技巧及常见误区。
一、异常处理基础
1. 什么是异常?
异常(Exception)是程序执行过程中发生的非预期事件,如文件未找到、空引用、除零错误等。C#中所有异常均派生自System.Exception类。
2. 核心结构:try-catch-finally
try
{
// 可能抛出异常的代码
File.ReadAllText("non_existent_file.txt");
}
catch (FileNotFoundException ex)
{
// 处理特定异常
Console.WriteLine($"文件未找到: {ex.FileName}");
}
catch (Exception ex)
{
// 通用异常处理
Console.WriteLine($"错误: {ex.Message}");
}
finally
{
// 无论是否发生异常都会执行
Console.WriteLine("资源清理...");
}
- try:包裹可能抛出异常的代码块。
- catch:捕获并处理特定或所有异常,支持多级捕获。
- finally:用于释放资源(如关闭文件、数据库连接),始终执行。
二、异常类型与自定义异常
1. 系统内置异常
- NullReferenceException:空引用错误。
- ArgumentException:无效参数。
- InvalidOperationException:对象状态不合法。
2. 创建自定义异常
public class InsufficientBalanceException : Exception
{
public decimal CurrentBalance { get; }
public InsufficientBalanceException(string message, decimal balance)
: base(message)
{
CurrentBalance = balance;
}
}
// 使用示例
throw new InsufficientBalanceException("余额不足", 50.0m);
- 继承自Exception类。
- 添加自定义属性以传递额外上下文信息。
三、高级异常处理技巧
1. 异常过滤器(C# 6.0+)
使用when关键字仅在特定条件时捕获异常:
try
{
// ...
}
catch (HttpRequestException ex) when (ex.StatusCode == 404)
{
Console.WriteLine("资源不存在");
}
2. 重新抛出异常
保留原始堆栈跟踪的两种方式:
catch (Exception ex)
{
// 方式1:直接throw
throw;
// 方式2:使用ExceptionDispatchInfo(跨上下文)
ExceptionDispatchInfo.Capture(ex).Throw();
}
避免throw ex,这会重置堆栈跟踪!
3. 异步与多线程异常处理
Task异常:通过AggregateException包装多个异常。
try
{
await Task.WhenAll(task1, task2);
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
Console.WriteLine(ex.Message);
}
}
四、最佳实践与性能优化
(1) 遵循“捕获具体异常”原则:避免笼统的catch (Exception),除非在顶层日志记录。
(2) 不要用异常控制流程:如用int.TryParse替代try-catch处理解析失败。
(3) 谨慎使用全局异常处理:
- 控制台应用:AppDomain.CurrentDomain.UnhandledException
- ASP.NET Core:中间件UseExceptionHandler
(4) 记录完整上下文包括异常消息、堆栈跟踪、输入参数等:
catch (Exception ex)
{
logger.LogError(ex, "操作失败。参数: {Param}", paramValue);
throw;
}
性能注意事项异常的构造和捕获成本较高。高频代码中避免依赖异常处理逻辑。
五、常见误区与解决方案
误区1:忽略异常(空的catch块)
修复:至少记录异常,或明确标记为已处理。
误区2:过度捕获异常
修复:仅在知道如何恢复时捕获,否则向上传递。
误区3:丢失原始异常上下文
修复:抛出自定义异常时,传递原始异常为内部异常:
throw new CustomException("操作失败", innerException: ex);
六、总结
C#的异常处理机制为开发者提供了强大的错误管理工具,但需合理使用以避免滥用。关键点包括:精准捕获异常、保留有效调试信息、资源清理的可靠性,以及在高性能场景下的谨慎使用。通过结合具体业务场景和本文的最佳实践,开发者可以构建出既健壮又高效的应用程序。