CodeGym /课程 /C# SELF /文件异常捕获 ( try-catch)...

文件异常捕获 ( try-catch)

C# SELF
第 38 级, 课程 1
可用

1. 介绍

对文件的操作几乎总是和程序外部的情况有关:文件可能被删除、移动、被另一个程序锁住,用户权限可能被撤销,或者磁盘上没有空闲空间。所有这些都可能导致异常——即在程序运行时中断并“抛出”一个异常对象(Exception)。如果不捕获并处理这个异常,程序会以错误结束,并向用户显示恐怖的红色控制台文本(或者更糟——悄无声息地崩溃)。

在 C# 中处理这类情况使用的是 try-catch 块(俗称“错误捕获”)。它不仅能“捕获”错误,还能让你优雅地决定接下来的动作:显示友好的消息、提示用户选另一个文件、把错误写入日志或简单地重试。

在真实应用里

如果你写的是一个控制台计算器,可能文件错误根本不会碰到。但如果你写的是企业文档处理程序或保存进度的游戏,不处理这些错误就像在走钢丝却没带安全网:迟早会出事。

2. 回顾异常处理

基本语法 try-catch

在进入具体的文件场景前,回顾一下 try-catch 的基本语法(我们在异常相关的讲座里已经见过):


try
{
    // 这里是可能“抛出”异常的代码
}
catch (Exception ex)
{
    // 这里我们捕获所有可能的异常... 但最好别这样做!
    Console.WriteLine("出了点问题: " + ex.Message);
}

在 try 块里写潜在危险的代码,在 catch 块里处理“如果出了问题该怎么办”。

程序员笑话:“try-catch 就像给错误戴上隐形斗篷。错误存在,但你看不到它!”

文件相关的异常

通过 .NET 操作文件时,最常见的是在创建流、读写时遇到异常。下面是几种典型场景及对应的异常:

  • 文件未找到 → FileNotFoundException
  • 没有对文件/目录的访问权限 → UnauthorizedAccessException
  • 路径问题(例如不允许的字符、路径太长) → PathTooLongException, ArgumentException
  • 文件被另一个进程占用 → IOException
  • 磁盘空间不足 → IOException

大多数与文件操作相关的异常都是 IOException 的派生类型。它们都继承自基类型 System.Exception。

3. 实践:捕获并处理文件操作错误

我们来看在“正在开发的应用”里的典型案例:假设我们尝试从文件读取一段问候语,处理它并输出给用户。这里我们会用 try-catch 来检测并处理错误。

示例 1:处理“文件未找到”


string filePath = "hello.txt";

try
{
    string greeting = File.ReadAllText(filePath);
    Console.WriteLine("文件内容: " + greeting);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("未找到文件!请检查文件 " + filePath + " 是否存在。");
    // 可以额外输出详细信息
    Console.WriteLine("技术细节: " + ex.Message);
}

如果文件 "hello.txt" 不存在,程序不会崩溃,而是输出友好的消息。就这么简单,让程序对用户的“傻错”更有韧性。

示例 2:权限被拒绝

再复杂一点的情况:文件存在,但我们没有读取权限(比如有人只给了写权限,或者文件在受保护的目录中)。


try
{
    string secret = File.ReadAllText("C:\\Windows\\System32\\config.txt");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("没有文件访问权限!尝试以管理员身份运行程序。");
    Console.WriteLine("技术原因: " + ex.Message);
}

示例 3:通用的 IOException

有些错误可能和锁冲突有关(例如,另一个进程正在占用文件),也可能是磁盘空间不足或硬件问题:


try
{
    File.WriteAllText("important.txt", "重要信息!");
}
catch (IOException ex)
{
    Console.WriteLine("文件操作出错:很可能被另一个程序占用或磁盘空间不足。");
    Console.WriteLine("技术原因: " + ex.Message);
}

4. 捕获多个异常:有选择地处理

有时候需要对不同异常做不同处理。.NET 允许写多个 catch 块——要从更具体的到更通用的顺序写(否则编译器会生气)。


try
{
    string content = File.ReadAllText("file.txt");
    Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("未找到文件。");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("没有文件访问权限!");
}
catch (IOException ex)
{
    Console.WriteLine("其他 IO 错误: " + ex.Message);
}

重要:如果把 catch (Exception ex) 放在最前面,后面的 catch 就没意义了,因为基类会先捕获一切!

5. 嵌套 try-catch 和重试

有时你不仅想处理错误,还想给用户“改正”的机会——比如提示用户输入一个存在的文件路径:


string filePath;
string content;
int attempts = 0;
const int maxAttempts = 3;

do
{
    Console.Write("请输入文件路径: ");
    filePath = Console.ReadLine();

    try
    {
        content = File.ReadAllText(filePath);
        Console.WriteLine("文件内容:\n" + content);
        break;
    }
    catch (FileNotFoundException)
    {
        Console.WriteLine("未找到文件!请再试一次。");
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("没有文件访问权限!请尝试其他文件。");
    }
    attempts++;
}
while (attempts < maxAttempts);

if (attempts == maxAttempts)
    Console.WriteLine("尝试次数过多。");

这段代码对忘了文件放哪儿的用户来说,是个小小的“友好界面”。

6. 常见错误与注意事项:从懒惰到有意识

新手(甚至有经验的开发者)经常犯的错误是捕获所有异常,写一个简单的 catch (Exception) 然后输出“发生了错误!”,不去考虑原因。这种做法有几个坏处。首先,它掩盖了业务逻辑中的真实问题。其次,和文件无关的其它错误(比如代码笔误或数学错误)可能被误吞,导致排查时间拉长。

更好的做法是只捕获你能有意义地处理的错误。如果不清楚发生了什么,宁可不捕获它——让异常“崩溃”程序,这样你能看到调用栈并更容易找到要修复的问题。

注意:有些异常可能包含内部原因(InnerException)。在写日志时分析它们很方便,能提供更详细的诊断信息。

另一个细节是:如果在 catch 之后程序无法继续(例如无法打开主配置文件),可以用 return 结束执行,或者重新抛出异常(throw;),这样不会留下半残的“僵尸程序”。

2
任务
C# SELF, 第 38 级, 课程 1
已锁定
处理写入文件时的多个异常
处理写入文件时的多个异常
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION