一次捕获多个异常的方法
技术背景
在 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))) { } 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 { } catch (Exception ex) { if (ex.GetType().IsAnyOf( typeof(FormatException), typeof(ArgumentException))) { } 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
关键字。