C# 异常处理

14.1 异常概述

异常是程序运行时发生的错误,会中断正常执行流程。

常见异常类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// NullReferenceException
string s = null;
int len = s.Length;

// IndexOutOfRangeException
int[] arr = { 1, 2, 3 };
int x = arr[5];

// DivideByZeroException
int y = 10 / 0;

// FormatException
int n = int.Parse("abc");

// FileNotFoundException
File.ReadAllText("nonexistent.txt");

14.2 try-catch 语句

基本语法

1
2
3
4
5
6
7
8
9
10
try
{
// 可能抛出异常的代码
int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
// 处理特定异常
Console.WriteLine($"除零错误: {ex.Message}");
}

多个 catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try
{
string input = Console.ReadLine();
int number = int.Parse(input);
int[] arr = new int[number];
arr[0] = 100;
}
catch (FormatException ex)
{
Console.WriteLine("格式错误: " + ex.Message);
}
catch (OverflowException ex)
{
Console.WriteLine("溢出错误: " + ex.Message);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("索引越界: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("其他错误: " + ex.Message);
}

14.3 finally 块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FileStream fs = null;
try
{
fs = new FileStream("test.txt", FileMode.Open);
// 读取文件
}
catch (FileNotFoundException ex)
{
Console.WriteLine("文件不存在");
}
finally
{
// 总是执行,用于清理资源
fs?.Close();
Console.WriteLine("清理完成");
}

14.4 throw 语句

抛出异常

1
2
3
4
5
6
7
8
9
10
11
public void SetAge(int age)
{
if (age < 0)
{
throw new ArgumentException("年龄不能为负数", nameof(age));
}
if (age > 150)
{
throw new ArgumentOutOfRangeException(nameof(age), "年龄不能超过 150");
}
}

重新抛出

1
2
3
4
5
6
7
8
9
10
11
12
try
{
ProcessData();
}
catch (Exception ex)
{
// 记录日志
Log.Error(ex);

// 重新抛出(保留堆栈)
throw;
}

14.5 自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[Serializable]
public class InsufficientFundsException : Exception
{
public decimal RequiredAmount { get; }
public decimal AvailableAmount { get; }

public InsufficientFundsException(decimal required, decimal available)
: base($"余额不足。需要: {required}, 可用: {available}")
{
RequiredAmount = required;
AvailableAmount = available;
}

public InsufficientFundsException(string message, Exception inner)
: base(message, inner) { }
}

// 使用
public void Withdraw(decimal amount)
{
if (amount > Balance)
{
throw new InsufficientFundsException(amount, Balance);
}
Balance -= amount;
}

14.6 异常筛选器(C# 6+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try
{
ProcessData();
}
catch (Exception ex) when (ex is IOException)
{
Console.WriteLine("IO 错误");
}
catch (Exception ex) when (ex.Message.Contains("timeout"))
{
Console.WriteLine("超时错误");
}
catch (Exception ex) when (LogException(ex))
{
// LogException 返回 false 时不进入 catch
}

bool LogException(Exception ex)
{
Log.Error(ex);
return false; // 不处理,继续抛出
}

14.7 最佳实践

1. 捕获具体异常

1
2
3
4
5
6
7
// 不推荐
try { } catch { }
try { } catch (Exception) { }

// 推荐
try { } catch (FileNotFoundException) { }
try { } catch (IOException ex) { }

2. 不要吞掉异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 不推荐
try
{
DoWork();
}
catch (Exception ex)
{
// 什么都不做
}

// 推荐
try
{
DoWork();
}
catch (Exception ex)
{
Log.Error(ex);
throw; // 或返回错误状态
}

3. 使用 using 自动释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 不推荐
FileStream fs = new FileStream("test.txt", FileMode.Open);
try
{
// 使用
}
finally
{
fs?.Close();
}

// 推荐
using (var fs = new FileStream("test.txt", FileMode.Open))
{
// 使用
} // 自动调用 Dispose

// C# 8+ using 声明
using var fs = new FileStream("test.txt", FileMode.Open);
// 方法结束时自动释放

14.8 本章小结

  • 异常使用 try-catch-finally 处理
  • catch 按顺序匹配,先具体后一般
  • finally 总是执行,用于清理资源
  • throw 抛出异常,throw; 重新抛出
  • 自定义异常继承 Exception
  • 异常筛选器 when 提供条件捕获
  • 使用 using 自动释放资源

练习题

  1. 编写安全除法方法,处理除零异常
  2. 创建自定义异常 InvalidEmailException
  3. 使用 try-catch-finally 读取文件
  4. 以下代码输出什么?
    1
    2
    3
    4
    try { Console.WriteLine("A"); throw new Exception(); }
    catch { Console.WriteLine("B"); }
    finally { Console.WriteLine("C"); }
    Console.WriteLine("D");
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
C#基础教程 系列
第 14/20 篇
分享: