-
try-catch-finally异常捕获
try-catch-finally异常捕获
概述
异常是程序运行时发生的意外错误(比如文件不存在、除以零、空指针访问),如果不处理,程序会直接崩溃并抛出错误信息。try-catch-finally是C#中处理异常的核心机制——它让程序在出错时优雅地vb.net教程C#教程python教程SQL教程access 2010教程处理错误,而不是直接终止,同时确保资源(如文件流、数据库连接)被正确释放。
-
基本结构
异常处理的核心结构由三部分组成:
try块:包裹可能抛出异常的代码(比如读写文件、网络请求);
catch块:捕获并处理try块中抛出的异常(可以有多个,按“具体→抽象”顺序排列);
finally块:不管try块是否抛出异常,一定会执行的代码(用于释放资源)。
语法模板:
csharp
try {
// 可能出错的代码
} catch (具体异常类型1 ex) {
// 处理异常1
} catch (具体异常类型2 ex) {
// 处理异常2
} catch (Exception ex) { // 最后捕获所有未处理的异常
// 处理通用异常
} finally {
// 必须执行的代码(释放资源)
}
-
实例代码:文件读取异常处理
以下是一个读取文件的实际场景,包含常见异常(文件不存在、权限不足、未知错误)的处理:
csharp
using System;
using System.IO; // 必须引用,处理文件操作
class FileReader {
static void Main() {
string filePath = "test.txt"; // 要读取的文件路径
StreamReader fileReader = null; // 文件流对象,初始化为null
try {
// -------------------------- try块:尝试执行可能出错的代码 --------------------------
// 打开文件(如果文件不存在,会抛出FileNotFoundException)
fileReader = new StreamReader(filePath);
// 读取文件内容(如果文件为空或无法读取,可能抛出其他异常)
string content = fileReader.ReadToEnd();
Console.WriteLine("文件读取成功!内容:\n" + content);
}
// -------------------------- catch块1:捕获“文件不存在”异常 --------------------------
catch (FileNotFoundException ex) {
// ex.Message:异常的描述信息;ex.StackTrace:错误的栈跟踪(方便调试)
Console.WriteLine($"错误:文件未找到 → {ex.Message}");
}
// -------------------------- catch块2:捕获“权限不足”异常 --------------------------
catch (UnauthorizedAccessException ex) {
Console.WriteLine($"错误:没有权限访问文件 → {ex.Message}");
}
// -------------------------- catch块3:捕获所有未处理的异常(最后用) --------------------------
catch (Exception ex) {
Console.WriteLine($"未知错误 → {ex.Message}");
Console.WriteLine("错误详情:\n" + ex.StackTrace); // 打印栈跟踪,帮助定位问题
}
// -------------------------- finally块:释放资源(一定会执行) --------------------------
finally {
// 检查文件流是否已创建,避免NullReferenceException
if (fileReader != null) {
fileReader.Close(); // 关闭文件流,释放资源
Console.WriteLine("\nfinally块执行:文件流已关闭");
}
}
}
}
2.1 逐行讲解
(1)初始化部分
string filePath = "test.txt";:指定要读取的文件路径;
StreamReader fileReader = null;:声明文件流对象,初始化为null(避免未初始化的错误)。
(2)try块
fileReader = new StreamReader(filePath);:尝试打开文件。如果文件不存在,直接抛出FileNotFoundException,跳转到对应的catch块;
string content = fileReader.ReadToEnd();:读取文件全部内容。如果文件被占用或无法读取,会抛出其他异常。
(3)catch块
顺序要求:catch块必须按“具体异常→抽象异常”排列。比如如果把catch(Exception ex)放在最前面,后面的FileNotFoundException永远不会被捕获(因为Exception是所有异常的基类);
异常对象ex:包含异常的详细信息,比如ex.Message(错误描述)、ex.StackTrace(错误发生的位置和调用栈)。
(4)finally块
核心作用:释放资源(文件流、数据库连接、网络连接等)。即使try块抛出异常,finally块也会执行;
注意:这里用if(fileReader != null)检查,因为如果文件不存在,fileReader还是null,直接调用Close()会抛出NullReferenceException。
-
手动抛出异常(throw)
除了系统自动抛出的异常,你也可以手动抛出异常(比如验证参数合法性时)。使用throw关键字:
实例:参数验证
csharp
static void RegisterUser(string username) {
if (string.IsNullOrEmpty(username)) {
// 手动抛出“参数为空”异常,提供详细错误信息
throw new ArgumentNullException("username", "用户名不能为空!");
}
Console.WriteLine($"用户{username}注册成功!");
}
// 调用方法
try {
RegisterUser(null); // 传入null,触发异常
} catch (ArgumentNullException ex) {
Console.WriteLine($"注册失败:{ex.Message}");
}
关键注意:
抛出异常时,要使用具体的异常类型(比如ArgumentNullException而不是通用的Exception),让调用者能明确处理;
不要用throw ex重置栈跟踪(会丢失原始错误位置),直接用throw保留原始信息:
csharp
catch (Exception ex) {
// 错误:重置栈跟踪
// throw ex;
// 正确:保留原始栈跟踪
throw;
}
-
基础知识拓展
4.1 异常的层次结构
C#中所有异常都继承自System.Exception基类,常见子类:
系统异常(System.SystemException子类):
oFileNotFoundException:文件不存在;
oNullReferenceException:访问空对象的成员;
oDivideByZeroException:除以零;
oUnauthorizedAccessException:权限不足;
应用异常(System.ApplicationException子类):自定义异常通常继承这个类(或直接继承Exception)。
4.2 自定义异常
如果系统提供的异常类型不够用(比如业务相关的错误,如“用户不存在”),可以自定义异常:
csharp
// 自定义异常:继承ApplicationException(或Exception)
public class UserNotFoundException : ApplicationException {
// 必须添加构造函数,传递错误信息到基类
public UserNotFoundException(string message) : base(message) {}
// 可选:添加带内部异常的构造函数
public UserNotFoundException(string message, Exception innerEx) : base(message, innerEx) {}
}
// 使用自定义异常
static void GetUser(int userId) {
if (userId != 1001) {
throw new UserNotFoundException($"用户ID {userId} 不存在!");
}
}
4.3 最佳实践
1.只捕获能处理的异常:不要用catch(Exception ex)捕获所有异常(除非你能处理所有情况),否则会掩盖未知错误;
2.用using释放资源:对于实现IDisposable接口的资源(如StreamReader、SqlConnection),用using语句替代finally块,更简洁:
csharp
try {
using (StreamReader reader = new StreamReader(filePath)) { // using自动释放资源
string content = reader.ReadToEnd();
}
} catch (FileNotFoundException ex) {
// 处理异常
}
3.记录异常日志:在catch块中记录异常信息(比如用Console.WriteLine(ex.StackTrace)或日志框架如NLog),方便调试;
4.提供有用的错误信息:抛出异常时,说明具体原因(比如“参数username不能为空”而不是“参数错误”);
5.不要吞掉异常:避免catch块中什么都不做(比如catch(Exception ex) {}),这会让错误无法被发现。
-
常见错误与避免
catch块顺序错误:把Exception放在前面,导致具体异常无法被捕获;
finally块抛异常:finally块中不要抛出新异常,否则会覆盖原始异常;
手动抛出通用异常:比如throw new Exception("错误"),应该用具体的异常类型;
忽略栈跟踪:用throw ex而不是throw,丢失原始错误位置。
总结
try-catch-finally是C#处理异常的核心——它让程序在出错时保持稳定,同时确保资源被正确释放。关键要点:
try:包裹可能出错的代码;
catch:按“具体→抽象”顺序捕获并处理异常;
finally:释放资源(或用using替代);
throw:手动抛出具体异常,保留栈跟踪;
最佳实践:只处理能解决的异常,记录日志,提供清晰的错误信息。
合理使用异常处理,能让你的程序更健壮、更易维护。
(本节完)
下一章:LINQ基础:查询集合数据
(深入讲解LINQ的查询语法与方法语法)
本站原创,转载请注明出处:










