在 C# 中,异常处理主要通过 try-catch-finally 语句来实现。这是应对运行时错误(如文件不存在、网络中断、空引用等)的标准方式。
下面我会用初级工程师容易理解的方式来讲解:
✅ 1. 基本结构:try-catch
try
{
// 可能会出错的代码("危险区")
int x = int.Parse("abc"); // 这里会抛出 FormatException
}
catch (FormatException ex)
{
// 捕获特定类型的异常,并处理
Console.WriteLine("输入格式不对:" + ex.Message);
}
try块:放可能抛出异常的代码。catch块:当try中发生异常时,程序跳到这里执行。- 可以有多个
catch,按从具体到一般的顺序写。
多个 catch 示例:
try
{
string s = null;
int length = s.Length; // 会抛出 NullReferenceException
}
catch (NullReferenceException ex)
{
Console.WriteLine("对象是 null:" + ex.Message);
}
catch (Exception ex) // 捕获所有其他异常(兜底)
{
Console.WriteLine("发生了未知错误:" + ex.Message);
}
⚠️ 注意:
catch (Exception)要放在最后,否则会"遮蔽"更具体的异常类型,编译器会报错!
✅ 2. finally 块(可选)
无论是否发生异常,finally 中的代码总会执行,通常用于清理资源(如关闭文件、数据库连接等)。
FileStream file = null;
try
{
file = File.Open("test.txt", FileMode.Open);
// 读取文件...
}
catch (FileNotFoundException ex)
{
Console.WriteLine("文件没找到:" + ex.Message);
}
finally
{
file?.Close(); // 确保文件被关闭
}
💡 在现代 C# 中,更推荐使用
using语句 自动释放资源(见下文)。
✅ 3. 抛出异常:throw
你也可以主动抛出异常,表示程序遇到了无法处理的问题:
public void CheckAge(int age)
{
if (age < 0)
{
throw new ArgumentException("年龄不能为负数!");
}
}
或者在 catch 中重新抛出异常(保留原始堆栈):
catch (Exception ex)
{
// 记录日志
LogError(ex);
throw; // ✅ 推荐:保留原始异常信息
// throw ex; ❌ 不推荐:会重置堆栈跟踪
}
✅ 4. 使用 using 自动释放资源(推荐替代 finally)
对于实现了 IDisposable 接口的对象(如文件流、数据库连接),用 using 更简洁安全:
using (var file = File.Open("test.txt", FileMode.Open))
{
// 使用 file...
} // 自动调用 file.Dispose(),即使发生异常也会关闭
C# 8.0+ 还支持 using 声明(更简洁):
using var file = File.Open("test.txt", FileMode.Open);
// 后续代码中使用 file
// 方法结束时自动释放
✅ 5. 常见异常类型(了解即可)
| 异常类型 | 触发场景 |
|---|---|
NullReferenceException |
访问了 null 对象的成员 |
IndexOutOfRangeException |
数组或列表索引越界 |
DivideByZeroException |
整数除以零 |
FileNotFoundException |
文件未找到 |
ArgumentException |
参数无效(开发者自定义) |
📌 最佳实践建议(初级也要知道!)
-
不要捕获所有异常然后忽略(空 catch 是坏习惯):
catch { } // ❌ 千万别这样! -
只捕获你能处理的异常,不能处理的就让上层处理。
-
记录异常日志(开发/生产环境很重要)。
-
避免用异常控制正常流程(比如用异常判断文件是否存在,效率低且不清晰)。
✅ 总结
C# 异常处理核心语法:
try
{
// 可能出错的代码
}
catch (SpecificException ex)
{
// 处理特定异常
}
catch (Exception ex)
{
// 处理其他异常(兜底)
}
finally
{
// 清理资源(可选)
}
配合 throw 和 using,就能写出健壮、安全的代码!
所有异常类型都继承自Exception类。
可以编写一个全局catch块。记住,始终将更具体的异常处理放在前面,更通用的放在后面。
问题
一个try后面可否有多个catch块?
可以。
如何确保某段代码抛出异常时仍然执行?
放在finaly。