VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • try-catch-finally异常捕获

try-catch-finally异常捕获
概述
异常是程序运行时发生的意外错误(比如文件不存在、除以零、空指针访问),如果不处理,程序会直接崩溃并抛出错误信息。try-catch-finally是C#中处理异常的核心机制——它让程序在出错时优雅地vb.net教程C#教程python教程SQL教程access 2010教程处理错误,而不是直接终止,同时确保资源(如文件流、数据库连接)被正确释放。

  1. 基本结构
    异常处理的核心结构由三部分组成:
    try块:包裹可能抛出异常的代码(比如读写文件、网络请求);
    catch块:捕获并处理try块中抛出的异常(可以有多个,按“具体→抽象”顺序排列);
    finally块:不管try块是否抛出异常,一定会执行的代码(用于释放资源)。
    语法模板:
    csharp
	try {
	// 可能出错的代码
	} catch (具体异常类型1 ex) {
	// 处理异常1
	} catch (具体异常类型2 ex) {
	// 处理异常2
	} catch (Exception ex) { // 最后捕获所有未处理的异常
	// 处理通用异常
	} finally {
	// 必须执行的代码(释放资源)
	}
  1. 实例代码:文件读取异常处理
    以下是一个读取文件的实际场景,包含常见异常(文件不存在、权限不足、未知错误)的处理:
    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。

  1. 手动抛出异常(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; 
	}
  1. 基础知识拓展
    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) {}),这会让错误无法被发现。

  1. 常见错误与避免
    catch块顺序错误:把Exception放在前面,导致具体异常无法被捕获;
    finally块抛异常:finally块中不要抛出新异常,否则会覆盖原始异常;
    手动抛出通用异常:比如throw new Exception("错误"),应该用具体的异常类型;
    忽略栈跟踪:用throw ex而不是throw,丢失原始错误位置。

总结
try-catch-finally是C#处理异常的核心——它让程序在出错时保持稳定,同时确保资源被正确释放。关键要点:
try:包裹可能出错的代码;
catch:按“具体→抽象”顺序捕获并处理异常;
finally:释放资源(或用using替代);
throw:手动抛出具体异常,保留栈跟踪;
最佳实践:只处理能解决的异常,记录日志,提供清晰的错误信息。
合理使用异常处理,能让你的程序更健壮、更易维护。
(本节完)
下一章:LINQ基础:查询集合数据
(深入讲解LINQ的查询语法与方法语法)

本站原创,转载请注明出处:


相关教程