一次捕获多个异常的方法

一次捕获多个异常的方法

技术背景

在 C# 开发中,处理异常是一个常见的需求。有时候我们希望对多种不同类型的异常执行相同的处理逻辑,如果为每种异常单独编写 catch 块会使代码变得冗长。因此,需要找到一种更简洁有效的方式来一次捕获多个异常。

实现步骤

1. 使用 if 语句在 catch 块中判断异常类型

1
2
3
4
5
6
7
8
9
catch (Exception ex)
{
if (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
else
throw;
}

这种方式简单直接,但当异常类型增多时,代码的可读性会变差。

2. 使用 C# 6.0 及以上的异常过滤器(Exception Filters)

1
2
3
4
5
6
7
8
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}

异常过滤器的优点是不会展开堆栈,并且在调试时能保留完整的堆栈信息。

3. 使用 C# 7.0 及以上的模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
catch (Exception ex)
{
switch (ex)
{
case FormatException _:
case OverflowException _:
WebId = Guid.Empty;
break;
default:
throw;
}
}

C# 8 可以省略丢弃变量,C# 9 可以使用更简洁的模式匹配语法:

1
2
3
4
5
6
7
8
9
10
11
catch (Exception ex)
{
switch (ex)
{
case FormatException or OverflowException:
WebId = Guid.Empty;
break;
default:
throw;
}
}

4. 使用 Guid.TryParse 方法避免异常

1
Guid.TryParse(queryString["web"], out WebId);

TryParse 方法不会抛出异常,如果格式错误会返回 false 并将 WebId 设置为 Guid.Empty

5. 使用扩展方法 IsAnyOf

1
2
3
4
5
6
7
8
if (ex.GetType().IsAnyOf(
typeof(FormatException),
typeof(ArgumentException)))
{
// Handle
}
else
throw;

需要定义 IsAnyOf 扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Common.FluentValidation
{
public static partial class Validate
{
public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
{
p_parameter.CannotBeNull("p_parameter");
p_comparisons.CannotBeNullOrEmpty("p_comparisons");

foreach (var item in p_comparisons)
if (p_parameter.Equals(item))
return true;

return false;
}
}
}

核心代码

以下是几种不同方法的完整示例代码:

异常过滤器示例

1
2
3
4
5
6
7
8
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}

模式匹配示例

1
2
3
4
5
6
7
8
9
10
11
catch (Exception ex)
{
switch (ex)
{
case FormatException or OverflowException:
WebId = Guid.Empty;
break;
default:
throw;
}
}

TryParse 示例

1
Guid.TryParse(queryString["web"], out WebId);

扩展方法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try
{
// Some code that may throw exceptions
}
catch (Exception ex)
{
if (ex.GetType().IsAnyOf(
typeof(FormatException),
typeof(ArgumentException)))
{
// Handle
}
else
throw;
}

最佳实践

  • 使用异常过滤器:在 C# 6.0 及以上版本中,优先使用异常过滤器,它能保持代码简洁,并且不会展开堆栈,在调试时更方便。
  • 使用 TryParse 方法:对于像 Guid 这样有 TryParse 方法的类型,优先使用该方法避免抛出异常,提高代码的健壮性。
  • 使用模式匹配:在 C# 7.0 及以上版本中,使用模式匹配可以使代码更具可读性和可维护性。

常见问题

1. 使用 if 语句在 catch 块中判断异常类型的缺点

当异常类型增多时,代码会变得冗长,可读性和可维护性变差。并且重新抛出异常会展开堆栈,影响调试信息。

2. 异常过滤器和 if 语句在 catch 块中的区别

异常过滤器不会展开堆栈,而 if 语句在 catch 块中重新抛出异常会展开堆栈。异常过滤器不能抛出异常,当条件不满足时会继续评估下一个 catch 条件。

3. 模式匹配在不同 C# 版本中的差异

C# 7.0 引入了基本的模式匹配语法,C# 8 可以省略丢弃变量,C# 9 提供了更简洁的模式匹配语法,如 or 关键字。


一次捕获多个异常的方法
https://119291.xyz/posts/2025-05-13.catch-multiple-exceptions-at-once/
作者
ww
发布于
2025年5月13日
许可协议