在.NET中,Exception类是所有异常的基类,提供错误消息、堆栈跟踪等关键信息,是错误处理的起点。捕获特定异常需使用try-catch块,按从具体到通用的顺序处理异常,避免仅捕获Exception类,必要时可自定义异常类,finally块用于资源清理等操作。
一、Exception类的作用- 基类地位:Exception类是.NET中所有运行时错误或异常事件的基类,定义了错误发生时携带的信息,如错误消息(Message)、堆栈跟踪(StackTrace)等。
- 信息丰富性:它不仅是一个简单的错误代码,而是一个包含丰富上下文信息的对象,能告知错误发生的位置、原因及触发因素,是编写健壮、可维护代码的关键。
二、捕获特定异常的方法try{ string input = "abc"; int number = int.Parse(input); Console.WriteLine($"转换成功: {number}");}catch (FormatException ex){ Console.WriteLine($"输入格式错误:{ex.Message}");}catch (OverflowException ex){ Console.WriteLine($"数字超出范围:{ex.Message}");}catch (Exception ex){ Console.WriteLine($"发生了未知错误:{ex.Message}");}finally{ Console.WriteLine("尝试转换操作完成。");}多catch块结构:能针对不同类型的错误采取不同的恢复策略,或提供更精确的错误信息。
不建议仅捕获Exception类的原因:
掩盖具体问题:只使用catch (Exception ex)会掩盖具体问题,导致代码难以调试和维护,可能引入新隐患。例如,文件操作和网络操作混合的代码,若只用一个catch (Exception ex)处理所有错误,难以区分是文件问题还是网络问题。
异常吞噬风险:有时开发者会捕获异常后什么都不做,导致潜在严重问题在后台悄无声息地发生,排查困难。
推荐做法:尽可能捕获预期会发生且能处理的特定异常,如文件操作捕获FileNotFoundException、UnauthorizedAccessException;网络操作捕获WebException、SocketException。对于无法预料或无法处理的异常,再用通用的catch (Exception ex)兜底,并记录详细错误信息。
三、自定义异常类- 适用场景:当标准库中提供的异常类型无法准确表达应用程序中发生的特定业务逻辑错误时,应考虑自定义异常类。
- 自定义异常的好处:
明确语义:异常名称本身说明错误性质,无需查看代码或注释。例如,电商系统中用户余额不足时,自定义InsufficientFundsException比使用InvalidOperationException或ArgumentException更能直接传达“余额不足”的业务含义。
携带更多信息:可在自定义异常中添加额外属性。如InsufficientFundsException中添加RequestedAmount和AvailableBalance属性,对捕获者处理异常很有帮助。
便于捕获和区分:调用者可精确捕获自定义异常,针对性处理业务逻辑错误,避免混淆在其他通用异常中。
提高可测试性:单元测试可更容易验证特定业务错误场景。
- 自定义异常示例:
public class InsufficientFundsException : Exception{ public decimal RequestedAmount { get; } public decimal AvailableBalance { get; } public InsufficientFundsException(decimal requestedAmount, decimal availableBalance) : base($"请求金额 {requestedAmount:C} 超过可用余额 {availableBalance:C}。") { RequestedAmount = requestedAmount; AvailableBalance = availableBalance; } public InsufficientFundsException(string message, Exception innerException) : base(message, innerException) { }}public void PurchaseItem(decimal itemPrice, decimal userBalance){ if (userBalance < itemPrice) { throw new InsufficientFundsException(itemPrice, userBalance); }}四、finally块的作用和使用场景System.IO.StreamReader reader = null;try{ reader = new System.IO.StreamReader("nonexistent.txt"); string line = reader.ReadLine(); Console.WriteLine(line);}catch (System.IO.FileNotFoundException ex){ Console.WriteLine($"文件未找到: {ex.Message}");}catch (Exception ex){ Console.WriteLine($"发生未知错误: {ex.Message}");}finally{ if (reader != null) { reader.Close(); Console.WriteLine("文件流已关闭。"); }}- 解锁:若在try块中获取了某个锁(如线程锁),finally块可保证锁被释放,避免死锁。
- 状态重置:在复杂操作中,临时改变全局或对象状态后,finally块可确保状态恢复到初始或预期状态。
- 日志记录:记录操作的最终状态,无论成功与否。
- using语句:对于实现了IDisposable接口的资源,C#提供using语句,它是try-finally块的语法糖,能确保IDisposable对象的Dispose()方法在离开using块时被调用,无论是否发生异常。例如:
try{ using (System.IO.StreamReader reader = new System.IO.StreamReader("nonexistent.txt")) { string line = reader.ReadLine(); Console.WriteLine(line); }}catch (System.IO.FileNotFoundException ex){ Console.WriteLine($"文件未找到: {ex.Message}");}catch (Exception ex){ Console.WriteLine($"发生未知错误: {ex.Message}");}