CodeGym /课程 /C# SELF /C#中的多重catch和过滤器

C#中的多重catch和过滤器

C# SELF
第 13 级 , 课程 4
可用

1. 入门

当你的程序比Hello, world!稍微复杂点时,你很快就会遇到这样的问题:不同类型的错误要用不同的方式处理。想象一下:你在操作文件、网络、数据库——每种出错的情况都得有不同的反应。比如,文件没找到可以让用户选别的文件,网络出错就可以重试或者弹个提示“检查下网线,是不是猫又把它咬断了”。

在C#里,这种情况就用多个catch块连着写。每个catch块专门处理自己那种异常(包括它的子类)。

多重catch的结构


try
{
    // 这里是可能抛出各种异常的代码
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"文件没找到: {ex.Message}");
}
catch (IOException ex) // 捕获所有输入/输出错误,除了FileNotFoundException
{
    Console.WriteLine($"输入输出错误: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"出问题了: {ex.Message}");
}
针对不同错误类型的多个 catch

注意!编译器会从上往下依次检查catch块,遇到第一个类型匹配的就用它。如果碰到FileNotFoundException,后面的catch就不会再走了。

“链条”示意图

异常类型 会被哪个catch块抓住?
FileNotFoundException 第一个catch
IOException(其他) 第二个catch
ArgumentException 第三个(通用)catch

为什么不能混着来?
最“宽泛”的catch——比如catch (Exception)——一定要放最后,否则它会提前“吞掉”所有异常,后面的专用catch根本用不上。

这就像大楼里所有烟雾报警器都关掉了,只因为有一个报警器被烤面包机触发了:后面真着火了系统也发现不了。

实用例子

using System;
using System.IO;

class Program
{
    static void Main()
    {
        try
        {
            double result = CalculateAverageAgeFromFile("users.txt");
            Console.WriteLine($"平均年龄 — {result}");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("错误:文件没找到。请检查路径。");
        }
        catch (FormatException ex)
        {
            Console.WriteLine("数据错误:无法读取年龄。");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"其他错误: {ex.Message}");
        }
    }

    static double CalculateAverageAgeFromFile(string filePath)
    {
        // (实现:读取文件,解析年龄,计算平均值)
        // ...
        throw new NotImplementedException();
    }
}

这里我们明确区分了:如果文件不存在(FileNotFoundException)怎么处理,如果文件里数据“有问题”(FormatException)又怎么处理。剩下的情况都交给第三个“兜底”块。

2. catch过滤器:捕捉细节

多个catch块很方便,但有时候还不够。有些情况下,同一种异常类型你还想根据具体情况做不同反应。

比如,网络不通如果是因为没流量了——可以试试重连。如果是服务器没响应——那就该弹别的提示了。

这时候就轮到catch过滤器登场了——C#的超级功能,可以不仅按类型,还能加条件来捕获异常。

when过滤器语法


catch (IOException ex) when (ex.Message.Contains("没有磁盘"))
{
    Console.WriteLine("哎呀!看起来你把U盘拔了。");
}
catch (IOException ex)
{
    Console.WriteLine("其他输入输出错误: " + ex.Message);
}
catch when过滤器细化捕获条件

这里第一个catch块只会抓到那些IOException,而且消息里包含"没有磁盘"的——其他的都进第二个块。

实际应用

过滤器在处理网络错误时特别有用,比如你要根据异常的内部属性决定:要不要重试,还是直接报错。

再举个例子:假如我们有个解析文件的方法,想要单独处理“年龄”字段出错(比如年龄写成了"abc"而不是数字),而不是所有FormatException都一样处理。

catch (FormatException ex) when (ex.Message.Contains("年龄"))
{
    Console.WriteLine("错误:无法读取年龄。请检查数据。");
}
catch (FormatException ex)
{
    Console.WriteLine("数据格式错误: " + ex.Message);
}

过滤器和性能

过滤器很方便,但要记住,过滤器里的条件是在进入catch块之前计算的,如果条件不成立,catch块里的代码根本不会执行。

顺便说下,如果过滤器表达式里自己抛了异常(比如when里除以零),这个异常当前catch永远不会捕获。要小心点。

3. 多个catch和过滤器一起用

想象一下,你的项目里不仅要按异常类型处理,还要看异常的内部状态。比如处理IOException时,如果是磁盘没空间和没有权限要分别处理。


try
{
    File.AppendAllText("log.txt", "新日志\n");
}
catch (IOException ex) when (ex.Message.Contains("没有空间"))
{
    Console.WriteLine("错误:磁盘空间用完了!");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("错误:没有写文件的权限。请用管理员身份运行。");
}
catch (IOException ex)
{
    Console.WriteLine("其他磁盘错误: " + ex.Message);
}
结合过滤器和不同类型 catch

这里我们既用了过滤器,也按错误类型分开处理。做复杂应用时,这种灵活性特别有用,可以更友好地应对各种常见故障。

4. 过滤器和多重catch的特点和“坑”

  • catch过滤器可以用异常变量(ex),但不能修改它。
  • 如果你在when里抛异常,这个catch块是不会捕获它的,异常会继续往上冒,就像过滤器不存在一样。
  • 过滤器逻辑成了代码契约的一部分:你的同事要知道,并不是所有IOException都会被抓住——只有条件成立才行。
  • 如果你整个方法只写了一个catch,但里面加了过滤器,有可能“多余”的异常根本没被捕获。所以不确定时,还是建议写多个明确的catch块。
  • 在大型应用里,可以用过滤器做集中式逻辑,比如只记录严重错误的日志。

5. 面试和实际工作场景

会用过滤器和多重catch,不仅仅是为了代码漂亮或者clean code高分。这是真正的技能,面试时尤其是做大型、复杂、分布式应用的岗位,面试官很可能会问到。

常见面试题:

  • 在C#里怎么处理文件读取的不同错误场景,让不同情况(没文件、磁盘满、数据坏了)显示不同提示?
  • 如果用了通用catch (Exception),怎么避免“吞掉”重要错误?
  • catch过滤器catch是干嘛用的?里面能不能有throw
2
任务
C# SELF, 第 13 级, 课程 4
已锁定
使用多个 `catch` 处理异常
使用多个 `catch` 处理异常
2
任务
C# SELF, 第 13 级, 课程 4
已锁定
使用 `catch` 过滤器
使用 `catch` 过滤器
1
调查/小测验
认识异常第 13 级,课程 4
不可用
认识异常
调用栈和自定义异常的创建
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION