Skip to content

C# 异常处理

本章将详细介绍 C# 中的异常处理机制,包括异常的概念、try-catch-finally语句、自定义异常、异常处理最佳实践等,帮助你编写更健壮和可靠的程序。

异常处理基础

什么是异常

csharp
// 异常是程序执行过程中发生的错误或意外情况
// C# 使用异常处理机制来处理运行时错误

using System;

static void ExceptionBasicsDemo()
{
    Console.WriteLine("=== 异常基础演示 ===");
    
    // 1. 常见的运行时异常
    Console.WriteLine("1. 除零异常:");
    try
    {
        int a = 10;
        int b = 0;
        int result = a / b;  // 抛出 DivideByZeroException
        Console.WriteLine($"结果: {result}");
    }
    catch (DivideByZeroException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
    
    // 2. 空引用异常
    Console.WriteLine("\n2. 空引用异常:");
    try
    {
        string text = null;
        int length = text.Length;  // 抛出 NullReferenceException
        Console.WriteLine($"长度: {length}");
    }
    catch (NullReferenceException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
    
    // 3. 索引超出范围异常
    Console.WriteLine("\n3. 索引超出范围异常:");
    try
    {
        int[] numbers = { 1, 2, 3 };
        int value = numbers[5];  // 抛出 IndexOutOfRangeException
        Console.WriteLine($"值: {value}");
    }
    catch (IndexOutOfRangeException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
    
    // 4. 格式异常
    Console.WriteLine("\n4. 格式异常:");
    try
    {
        string input = "abc";
        int number = int.Parse(input);  // 抛出 FormatException
        Console.WriteLine($"数字: {number}");
    }
    catch (FormatException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
    
    // 5. 参数异常
    Console.WriteLine("\n5. 参数异常:");
    try
    {
        string text = "Hello";
        string substring = text.Substring(-1, 5);  // 抛出 ArgumentOutOfRangeException
        Console.WriteLine($"子字符串: {substring}");
    }
    catch (ArgumentOutOfRangeException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
}

try-catch-finally 语句

csharp
using System;
using System.IO;

static void TryCatchFinallyDemo()
{
    Console.WriteLine("=== try-catch-finally 演示 ===");
    
    // 基本 try-catch 结构
    Console.WriteLine("1. 基本 try-catch:");
    try
    {
        Console.Write("请输入一个数字: ");
        string input = "42"; // 模拟输入
        int number = int.Parse(input);
        Console.WriteLine($"您输入的数字是: {number}");
    }
    catch (FormatException)
    {
        Console.WriteLine("输入格式错误,请输入有效数字");
    }
    catch (OverflowException)
    {
        Console.WriteLine("输入的数字超出范围");
    }
    
    // 多个 catch 块
    Console.WriteLine("\n2. 多个 catch 块:");
    try
    {
        ProcessArray();
    }
    catch (ArgumentNullException ex)
    {
        Console.WriteLine($"参数为空: {ex.ParamName}");
    }
    catch (IndexOutOfRangeException ex)
    {
        Console.WriteLine($"索引超出范围: {ex.Message}");
    }
    catch (Exception ex)  // 捕获所有其他异常
    {
        Console.WriteLine($"发生未知异常: {ex.GetType().Name} - {ex.Message}");
    }
    
    // try-catch-finally 结构
    Console.WriteLine("\n3. try-catch-finally:");
    FileStream fileStream = null;
    try
    {
        Console.WriteLine("尝试打开文件...");
        // fileStream = new FileStream("nonexistent.txt", FileMode.Open);
        throw new FileNotFoundException("模拟文件未找到");
    }
    catch (FileNotFoundException ex)
    {
        Console.WriteLine($"文件操作异常: {ex.Message}");
    }
    catch (UnauthorizedAccessException ex)
    {
        Console.WriteLine($"访问权限异常: {ex.Message}");
    }
    finally
    {
        // finally 块总是会执行,用于清理资源
        Console.WriteLine("执行清理操作...");
        fileStream?.Close();
        Console.WriteLine("资源已释放");
    }
    
    // 嵌套 try-catch
    Console.WriteLine("\n4. 嵌套 try-catch:");
    try
    {
        Console.WriteLine("外层 try 块");
        try
        {
            Console.WriteLine("内层 try 块");
            throw new InvalidOperationException("内层异常");
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"内层 catch: {ex.Message}");
            throw new ApplicationException("外层异常", ex);  // 重新抛出包装异常
        }
    }
    catch (ApplicationException ex)
    {
        Console.WriteLine($"外层 catch: {ex.Message}");
        if (ex.InnerException != null)
        {
            Console.WriteLine($"内部异常: {ex.InnerException.Message}");
        }
    }
}

static void ProcessArray()
{
    int[] numbers = null;
    
    // 模拟不同的异常情况
    Random random = new Random();
    int scenario = random.Next(1, 4);
    
    switch (scenario)
    {
        case 1:
            numbers = null;
            int length = numbers.Length;  // ArgumentNullException
            break;
        case 2:
            numbers = new int[] { 1, 2, 3 };
            int value = numbers[10];  // IndexOutOfRangeException
            break;
        case 3:
            throw new NotImplementedException("功能尚未实现");
    }
}

异常信息和堆栈跟踪

csharp
using System;
using System.Diagnostics;

static void ExceptionInformationDemo()
{
    Console.WriteLine("=== 异常信息演示 ===");
    
    try
    {
        Method1();
    }
    catch (Exception ex)
    {
        Console.WriteLine("=== 异常详细信息 ===");
        Console.WriteLine($"异常类型: {ex.GetType().FullName}");
        Console.WriteLine($"异常消息: {ex.Message}");
        Console.WriteLine($"异常源: {ex.Source}");
        Console.WriteLine($"目标站点: {ex.TargetSite}");
        
        Console.WriteLine("\n=== 堆栈跟踪 ===");
        Console.WriteLine(ex.StackTrace);
        
        // 检查内部异常
        if (ex.InnerException != null)
        {
            Console.WriteLine("\n=== 内部异常 ===");
            Console.WriteLine($"内部异常类型: {ex.InnerException.GetType().FullName}");
            Console.WriteLine($"内部异常消息: {ex.InnerException.Message}");
        }
        
        // 异常数据
        if (ex.Data.Count > 0)
        {
            Console.WriteLine("\n=== 异常数据 ===");
            foreach (var key in ex.Data.Keys)
            {
                Console.WriteLine($"{key}: {ex.Data[key]}");
            }
        }
    }
}

static void Method1()
{
    Console.WriteLine("进入 Method1");
    Method2();
}

static void Method2()
{
    Console.WriteLine("进入 Method2");
    Method3();
}

static void Method3()
{
    Console.WriteLine("进入 Method3");
    
    try
    {
        // 模拟一个复杂的异常情况
        var ex = new InvalidOperationException("Method3 中发生错误");
        ex.Data.Add("ErrorCode", "ERR001");
        ex.Data.Add("Timestamp", DateTime.Now);
        ex.Data.Add("UserId", "12345");
        throw ex;
    }
    catch (InvalidOperationException ex)
    {
        // 包装异常并重新抛出
        throw new ApplicationException("Method3 处理失败", ex);
    }
}

自定义异常

创建自定义异常类

csharp
// 自定义异常应该继承自 Exception 或其子类
// 遵循异常命名约定:以 Exception 结尾

using System;
using System.Runtime.Serialization;

// 基本自定义异常
public class BankAccountException : Exception
{
    public string AccountNumber { get; }
    
    public BankAccountException() : base() { }
    
    public BankAccountException(string message) : base(message) { }
    
    public BankAccountException(string message, Exception innerException) 
        : base(message, innerException) { }
    
    public BankAccountException(string accountNumber, string message) 
        : base(message)
    {
        AccountNumber = accountNumber;
    }
    
    // 序列化构造函数(用于跨应用程序域传递异常)
    protected BankAccountException(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {
        AccountNumber = info.GetString(nameof(AccountNumber));
    }
    
    // 重写序列化方法
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue(nameof(AccountNumber), AccountNumber);
    }
}

// 具体的业务异常
public class InsufficientFundsException : BankAccountException
{
    public decimal RequestedAmount { get; }
    public decimal AvailableBalance { get; }
    
    public InsufficientFundsException(string accountNumber, decimal requestedAmount, decimal availableBalance)
        : base(accountNumber, $"账户 {accountNumber} 余额不足。请求金额: {requestedAmount:C}, 可用余额: {availableBalance:C}")
    {
        RequestedAmount = requestedAmount;
        AvailableBalance = availableBalance;
    }
}

public class AccountNotFoundException : BankAccountException
{
    public AccountNotFoundException(string accountNumber)
        : base(accountNumber, $"未找到账户: {accountNumber}")
    {
    }
}

public class AccountFrozenException : BankAccountException
{
    public DateTime FrozenDate { get; }
    public string Reason { get; }
    
    public AccountFrozenException(string accountNumber, DateTime frozenDate, string reason)
        : base(accountNumber, $"账户 {accountNumber} 已被冻结。冻结日期: {frozenDate:yyyy-MM-dd}, 原因: {reason}")
    {
        FrozenDate = frozenDate;
        Reason = reason;
    }
}

// 银行账户类
public class BankAccount
{
    private string accountNumber;
    private decimal balance;
    private bool isFrozen;
    private DateTime? frozenDate;
    private string frozenReason;
    
    public BankAccount(string accountNumber, decimal initialBalance = 0)
    {
        this.accountNumber = accountNumber ?? throw new ArgumentNullException(nameof(accountNumber));
        this.balance = initialBalance;
        this.isFrozen = false;
    }
    
    public string AccountNumber => accountNumber;
    public decimal Balance => balance;
    public bool IsFrozen => isFrozen;
    
    public void Deposit(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("存款金额必须大于零", nameof(amount));
        
        if (isFrozen)
            throw new AccountFrozenException(accountNumber, frozenDate.Value, frozenReason);
        
        balance += amount;
        Console.WriteLine($"账户 {accountNumber} 存款 {amount:C},当前余额: {balance:C}");
    }
    
    public void Withdraw(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("取款金额必须大于零", nameof(amount));
        
        if (isFrozen)
            throw new AccountFrozenException(accountNumber, frozenDate.Value, frozenReason);
        
        if (amount > balance)
            throw new InsufficientFundsException(accountNumber, amount, balance);
        
        balance -= amount;
        Console.WriteLine($"账户 {accountNumber} 取款 {amount:C},当前余额: {balance:C}");
    }
    
    public void FreezeAccount(string reason)
    {
        isFrozen = true;
        frozenDate = DateTime.Now;
        frozenReason = reason;
        Console.WriteLine($"账户 {accountNumber} 已冻结,原因: {reason}");
    }
    
    public void UnfreezeAccount()
    {
        isFrozen = false;
        frozenDate = null;
        frozenReason = null;
        Console.WriteLine($"账户 {accountNumber} 已解冻");
    }
}

// 银行服务类
public class BankService
{
    private Dictionary<string, BankAccount> accounts;
    
    public BankService()
    {
        accounts = new Dictionary<string, BankAccount>();
    }
    
    public void CreateAccount(string accountNumber, decimal initialBalance = 0)
    {
        if (accounts.ContainsKey(accountNumber))
            throw new InvalidOperationException($"账户 {accountNumber} 已存在");
        
        accounts[accountNumber] = new BankAccount(accountNumber, initialBalance);
        Console.WriteLine($"成功创建账户 {accountNumber},初始余额: {initialBalance:C}");
    }
    
    public BankAccount GetAccount(string accountNumber)
    {
        if (!accounts.TryGetValue(accountNumber, out BankAccount account))
            throw new AccountNotFoundException(accountNumber);
        
        return account;
    }
    
    public void Transfer(string fromAccount, string toAccount, decimal amount)
    {
        if (fromAccount == toAccount)
            throw new ArgumentException("不能向同一账户转账");
        
        var from = GetAccount(fromAccount);
        var to = GetAccount(toAccount);
        
        // 使用事务性操作
        try
        {
            from.Withdraw(amount);
            to.Deposit(amount);
            Console.WriteLine($"转账成功: {fromAccount} -> {toAccount}, 金额: {amount:C}");
        }
        catch (Exception)
        {
            // 如果转账失败,这里可以实现回滚逻辑
            throw;
        }
    }
}

static void CustomExceptionDemo()
{
    Console.WriteLine("=== 自定义异常演示 ===");
    
    BankService bank = new BankService();
    
    try
    {
        // 创建账户
        bank.CreateAccount("ACC001", 1000);
        bank.CreateAccount("ACC002", 500);
        
        var account1 = bank.GetAccount("ACC001");
        var account2 = bank.GetAccount("ACC002");
        
        // 正常操作
        Console.WriteLine("\n=== 正常操作 ===");
        account1.Deposit(200);
        account1.Withdraw(150);
        
        // 测试各种异常情况
        Console.WriteLine("\n=== 异常情况测试 ===");
        
        // 1. 余额不足异常
        try
        {
            account2.Withdraw(1000);
        }
        catch (InsufficientFundsException ex)
        {
            Console.WriteLine($"余额不足: {ex.Message}");
            Console.WriteLine($"请求金额: {ex.RequestedAmount:C}, 可用余额: {ex.AvailableBalance:C}");
        }
        
        // 2. 账户冻结异常
        try
        {
            account1.FreezeAccount("可疑交易");
            account1.Withdraw(100);
        }
        catch (AccountFrozenException ex)
        {
            Console.WriteLine($"账户冻结: {ex.Message}");
            Console.WriteLine($"冻结日期: {ex.FrozenDate:yyyy-MM-dd HH:mm:ss}");
        }
        
        // 3. 账户未找到异常
        try
        {
            bank.GetAccount("NONEXISTENT");
        }
        catch (AccountNotFoundException ex)
        {
            Console.WriteLine($"账户未找到: {ex.Message}");
        }
        
        // 4. 转账异常
        try
        {
            bank.Transfer("ACC001", "ACC002", 500);  // account1 已冻结
        }
        catch (AccountFrozenException ex)
        {
            Console.WriteLine($"转账失败 - 账户冻结: {ex.Message}");
        }
        
    }
    catch (Exception ex)
    {
        Console.WriteLine($"未处理的异常: {ex.GetType().Name} - {ex.Message}");
    }
}

异常处理最佳实践

异常处理原则

csharp
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

// 1. 使用 using 语句自动释放资源
public class ResourceManagementExample
{
    // ❌ 不好的做法
    public static string ReadFileBad(string filePath)
    {
        FileStream stream = null;
        StreamReader reader = null;
        
        try
        {
            stream = new FileStream(filePath, FileMode.Open);
            reader = new StreamReader(stream);
            return reader.ReadToEnd();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取文件失败: {ex.Message}");
            return null;
        }
        finally
        {
            reader?.Close();
            stream?.Close();
        }
    }
    
    // ✅ 好的做法
    public static string ReadFileGood(string filePath)
    {
        try
        {
            using (var stream = new FileStream(filePath, FileMode.Open))
            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine($"文件未找到: {filePath}");
            return null;
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine($"没有访问权限: {filePath}");
            return null;
        }
        catch (IOException ex)
        {
            Console.WriteLine($"IO错误: {ex.Message}");
            return null;
        }
    }
    
    // ✅ 更好的做法 - 使用现代API
    public static async Task<string> ReadFileAsync(string filePath)
    {
        try
        {
            return await File.ReadAllTextAsync(filePath);
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine($"文件未找到: {filePath}");
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取文件失败: {ex.Message}");
            throw; // 重新抛出未知异常
        }
    }
}

// 2. 异常过滤器 (Exception Filters)
public class ExceptionFilterExample
{
    public static void ProcessData(string data)
    {
        try
        {
            // 模拟数据处理
            if (string.IsNullOrEmpty(data))
                throw new ArgumentException("数据不能为空");
            
            if (data.Length > 1000)
                throw new ArgumentException("数据过长");
            
            // 处理数据...
            Console.WriteLine($"处理数据: {data}");
        }
        catch (ArgumentException ex) when (ex.Message.Contains("数据不能为空"))
        {
            Console.WriteLine("处理空数据异常");
            // 使用默认数据
        }
        catch (ArgumentException ex) when (ex.Message.Contains("数据过长"))
        {
            Console.WriteLine("处理数据过长异常");
            // 截断数据
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine($"其他参数异常: {ex.Message}");
        }
    }
    
    // 使用异常过滤器进行日志记录
    public static bool LogException(Exception ex)
    {
        Console.WriteLine($"[LOG] 异常: {ex.GetType().Name} - {ex.Message}");
        return false; // 返回 false 表示不处理异常,继续向上传播
    }
    
    public static void ProcessWithLogging(string data)
    {
        try
        {
            ProcessData(data);
        }
        catch (Exception ex) when (LogException(ex))
        {
            // 这个 catch 块永远不会执行,因为 LogException 返回 false
            // 但异常会被记录
        }
    }
}

// 3. 异常重试机制
public class RetryExample
{
    public static async Task<string> DownloadWithRetry(string url, int maxRetries = 3)
    {
        using (var client = new HttpClient())
        {
            for (int attempt = 1; attempt <= maxRetries; attempt++)
            {
                try
                {
                    Console.WriteLine($"尝试下载 (第 {attempt} 次): {url}");
                    var response = await client.GetStringAsync(url);
                    Console.WriteLine("下载成功");
                    return response;
                }
                catch (HttpRequestException ex) when (attempt < maxRetries)
                {
                    Console.WriteLine($"下载失败 (第 {attempt} 次): {ex.Message}");
                    
                    // 指数退避策略
                    int delay = (int)Math.Pow(2, attempt) * 1000; // 2^attempt 秒
                    Console.WriteLine($"等待 {delay}ms 后重试...");
                    await Task.Delay(delay);
                }
                catch (HttpRequestException ex) when (attempt == maxRetries)
                {
                    Console.WriteLine($"下载最终失败: {ex.Message}");
                    throw new ApplicationException($"在 {maxRetries} 次尝试后下载失败", ex);
                }
            }
            
            return null; // 永远不会到达这里
        }
    }
}

// 4. 异常聚合
public class AggregateExceptionExample
{
    public static async Task ProcessMultipleTasksAsync()
    {
        var tasks = new List<Task>
        {
            Task.Run(() => throw new InvalidOperationException("任务1失败")),
            Task.Run(() => throw new ArgumentException("任务2失败")),
            Task.Run(() => Console.WriteLine("任务3成功")),
            Task.Run(() => throw new NotImplementedException("任务4失败"))
        };
        
        try
        {
            await Task.WhenAll(tasks);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"捕获到异常: {ex.GetType().Name} - {ex.Message}");
            
            // 检查是否是 AggregateException
            if (ex is AggregateException aggEx)
            {
                Console.WriteLine($"聚合异常包含 {aggEx.InnerExceptions.Count} 个内部异常:");
                
                foreach (var innerEx in aggEx.InnerExceptions)
                {
                    Console.WriteLine($"  - {innerEx.GetType().Name}: {innerEx.Message}");
                }
                
                // 处理特定类型的异常
                aggEx.Handle(innerEx =>
                {
                    if (innerEx is InvalidOperationException)
                    {
                        Console.WriteLine($"处理 InvalidOperationException: {innerEx.Message}");
                        return true; // 表示已处理
                    }
                    return false; // 表示未处理,将重新抛出
                });
            }
        }
    }
}

static async Task BestPracticesDemo()
{
    Console.WriteLine("=== 异常处理最佳实践演示 ===");
    
    // 1. 资源管理
    Console.WriteLine("1. 资源管理:");
    string content = ResourceManagementExample.ReadFileGood("test.txt");
    
    // 2. 异常过滤器
    Console.WriteLine("\n2. 异常过滤器:");
    ExceptionFilterExample.ProcessData("");
    ExceptionFilterExample.ProcessData("正常数据");
    
    // 3. 重试机制
    Console.WriteLine("\n3. 重试机制:");
    try
    {
        // 注意:这会实际尝试网络请求,可能失败
        // await RetryExample.DownloadWithRetry("https://httpstat.us/500");
        Console.WriteLine("重试机制演示已跳过(避免实际网络请求)");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"重试最终失败: {ex.Message}");
    }
    
    // 4. 聚合异常
    Console.WriteLine("\n4. 聚合异常:");
    await AggregateExceptionExample.ProcessMultipleTasksAsync();
}

异常处理反模式

csharp
// 常见的异常处理错误和正确做法

public class ExceptionAntiPatterns
{
    // ❌ 反模式1: 吞噬异常
    public static void BadExample1()
    {
        try
        {
            // 一些可能失败的操作
            int result = 10 / 0;
        }
        catch
        {
            // 什么都不做 - 这是非常糟糕的做法!
        }
    }
    
    // ✅ 正确做法1: 适当处理或重新抛出
    public static void GoodExample1()
    {
        try
        {
            int result = 10 / 0;
        }
        catch (DivideByZeroException ex)
        {
            // 记录异常
            Console.WriteLine($"除零错误: {ex.Message}");
            
            // 根据情况决定是否重新抛出
            throw; // 或者处理后返回默认值
        }
    }
    
    // ❌ 反模式2: 捕获 Exception 而不是具体异常
    public static void BadExample2(string input)
    {
        try
        {
            int number = int.Parse(input);
            Console.WriteLine($"数字: {number}");
        }
        catch (Exception ex)
        {
            Console.WriteLine("发生错误"); // 信息不够具体
        }
    }
    
    // ✅ 正确做法2: 捕获具体异常类型
    public static void GoodExample2(string input)
    {
        try
        {
            int number = int.Parse(input);
            Console.WriteLine($"数字: {number}");
        }
        catch (ArgumentNullException)
        {
            Console.WriteLine("输入不能为空");
        }
        catch (FormatException)
        {
            Console.WriteLine("输入格式不正确,请输入有效数字");
        }
        catch (OverflowException)
        {
            Console.WriteLine("输入的数字超出范围");
        }
    }
    
    // ❌ 反模式3: 使用异常控制程序流程
    public static int BadExample3(string[] array, string target)
    {
        try
        {
            for (int i = 0; ; i++)
            {
                if (array[i] == target)
                    return i;
            }
        }
        catch (IndexOutOfRangeException)
        {
            return -1; // 使用异常来结束循环
        }
    }
    
    // ✅ 正确做法3: 使用正常的控制结构
    public static int GoodExample3(string[] array, string target)
    {
        for (int i = 0; i < array.Length; i++)
        {
            if (array[i] == target)
                return i;
        }
        return -1;
    }
    
    // ❌ 反模式4: 抛出 Exception 而不是具体异常
    public static void BadExample4(int age)
    {
        if (age < 0)
            throw new Exception("年龄不能为负数");
    }
    
    // ✅ 正确做法4: 抛出具体的异常类型
    public static void GoodExample4(int age)
    {
        if (age < 0)
            throw new ArgumentOutOfRangeException(nameof(age), age, "年龄不能为负数");
    }
    
    // ❌ 反模式5: 丢失原始异常信息
    public static void BadExample5()
    {
        try
        {
            SomeMethodThatThrows();
        }
        catch (Exception ex)
        {
            throw new ApplicationException("操作失败"); // 丢失了原始异常
        }
    }
    
    // ✅ 正确做法5: 保留原始异常信息
    public static void GoodExample5()
    {
        try
        {
            SomeMethodThatThrows();
        }
        catch (Exception ex)
        {
            throw new ApplicationException("操作失败", ex); // 保留内部异常
        }
    }
    
    private static void SomeMethodThatThrows()
    {
        throw new InvalidOperationException("原始错误");
    }
}

// 异常处理工具类
public static class ExceptionHelper
{
    // 安全执行操作,返回结果或默认值
    public static T SafeExecute<T>(Func<T> operation, T defaultValue = default(T))
    {
        try
        {
            return operation();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"操作失败: {ex.Message}");
            return defaultValue;
        }
    }
    
    // 安全执行操作,返回成功标志
    public static bool TryExecute(Action operation, out Exception exception)
    {
        exception = null;
        try
        {
            operation();
            return true;
        }
        catch (Exception ex)
        {
            exception = ex;
            return false;
        }
    }
    
    // 重试执行操作
    public static T RetryExecute<T>(Func<T> operation, int maxRetries = 3, int delayMs = 1000)
    {
        Exception lastException = null;
        
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                return operation();
            }
            catch (Exception ex)
            {
                lastException = ex;
                
                if (attempt < maxRetries)
                {
                    Console.WriteLine($"尝试 {attempt} 失败,{delayMs}ms 后重试: {ex.Message}");
                    Thread.Sleep(delayMs);
                }
            }
        }
        
        throw new AggregateException($"在 {maxRetries} 次尝试后操作仍然失败", lastException);
    }
}

static void AntiPatternsDemo()
{
    Console.WriteLine("=== 异常处理反模式演示 ===");
    
    // 演示正确的异常处理
    Console.WriteLine("1. 具体异常处理:");
    ExceptionAntiPatterns.GoodExample2("abc");
    ExceptionAntiPatterns.GoodExample2("123");
    
    // 演示工具类使用
    Console.WriteLine("\n2. 异常处理工具:");
    
    // 安全执行
    int result = ExceptionHelper.SafeExecute(() => int.Parse("abc"), -1);
    Console.WriteLine($"安全解析结果: {result}");
    
    // 尝试执行
    bool success = ExceptionHelper.TryExecute(() => 
    {
        int x = 10 / 0;
    }, out Exception ex);
    
    Console.WriteLine($"执行结果: {success}");
    if (!success)
    {
        Console.WriteLine($"异常: {ex.Message}");
    }
    
    // 重试执行
    try
    {
        int retryResult = ExceptionHelper.RetryExecute(() =>
        {
            if (new Random().Next(1, 4) != 3) // 66% 失败率
                throw new InvalidOperationException("随机失败");
            return 42;
        }, maxRetries: 3, delayMs: 500);
        
        Console.WriteLine($"重试成功,结果: {retryResult}");
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($"重试最终失败: {ex.Message}");
    }
}

本章小结

本章详细介绍了 C# 中的异常处理:

关键要点:

  • 异常概念:程序运行时发生的错误或意外情况
  • try-catch-finally:异常处理的基本语法结构
  • 异常层次:所有异常都继承自 Exception 类
  • 自定义异常:创建特定于业务逻辑的异常类型
  • 异常信息:Message、StackTrace、InnerException 等

异常处理原则:

  • 具体性:捕获具体的异常类型而不是 Exception
  • 适度性:只在能够处理的地方捕获异常
  • 信息保留:保留原始异常信息和堆栈跟踪
  • 资源清理:使用 finally 或 using 确保资源释放
  • 性能考虑:异常处理有性能开销,不应用于控制流程

最佳实践:

  • 使用 using 语句自动管理资源
  • 异常过滤器提供更精确的异常处理
  • 实现重试机制处理临时性错误
  • 避免吞噬异常或过度捕获
  • 创建有意义的自定义异常类型

常见反模式:

  • 空的 catch 块(吞噬异常)
  • 捕获 Exception 而不是具体异常
  • 使用异常控制程序流程
  • 抛出 Exception 而不是具体异常类型
  • 丢失原始异常信息

异常处理策略:

  • 快速失败:尽早发现和报告错误
  • 优雅降级:在出错时提供备选方案
  • 重试机制:处理临时性错误
  • 断路器模式:防止级联失败

下一步: 在下一章中,我们将学习 LINQ(语言集成查询),了解如何使用声明式语法查询和操作数据。

延伸阅读

本站内容仅供学习和研究使用。