模式匹配
C#模式匹配与switch表达式
概述
C# 的模式匹配从 C# 7 开始引入,经过多个版本的增强,已成为表达条件逻辑的强大工具。模式匹配支持类型检查、属性解构、列表匹配和逻辑组合,配合 switch 表达式可以写出简洁而安全的分支代码。
基础概念
模式类型
| 模式类型 | 语法 | 版本 | 说明 |
|---|---|---|---|
| 类型模式 | is Type | C# 7 | 检查类型并提取值 |
| 属性模式 | is { Prop: val } | C# 8 | 检查属性值 |
| 位置模式 | is (a, b) | C# 8 | 基于解构匹配 |
| 列表模式 | is [1, 2, .., 5] | C# 11 | 匹配数组/列表元素 |
| 逻辑模式 | and, or, not | C# 9 | 组合模式 |
| 关系模式 | is > 0 | C# 9 | 比较值 |
快速上手
类型模式
// is 类型检查 + 智能转换
if (obj is string s) {
Console.WriteLine(s.Length); // s 已自动转换为 string
}
// switch 中的类型模式
string Describe(object obj) => obj switch {
int i => $"整数: {i}",
double d => $"浮点数: {d:F2}",
string s => $"字符串: {s}",
null => "null",
_ => "未知类型" // _ 是弃元模式,匹配任何值
};
属性模式
// 检查对象属性
if (person is { Age: >= 18, City: "北京" }) {
Console.WriteLine("北京成年用户");
}
// 嵌套属性模式
if (order is { Customer.VipLevel: >= 3, TotalAmount: > 1000 }) {
ApplyDiscount();
}
// switch 表达式中的属性模式
string Categorize(Person p) => p switch {
{ Age: < 13 } => "儿童",
{ Age: >= 13 and < 20 } => "青少年",
{ Age: >= 20 and < 60 } => "成年人",
{ Age: >= 60 } => "老年人",
};
switch 表达式
// switch 表达式(C# 8+)
string label = statusCode switch {
200 => "OK",
404 => "Not Found",
>= 500 => "Server Error",
_ => "Unknown"
};
// 元组模式
string GetDirection(int x, int y) => (x, y) switch {
(0, 0) => "原点",
(> 0, 0) => "正X轴",
(0, > 0) => "正Y轴",
(> 0, > 0) => "第一象限",
(< 0, > 0) => "第二象限",
_ => "其他"
};
详细用法
位置模式与解构
// 记录类型自动支持位置模式
public record Point(double X, double Y);
public record Circle(Point Center, double Radius);
string DescribeShape(Shape shape) => shape switch {
Circle({ X: 0, Y: 0 }, var r) => $"以原点为圆心,半径 {r}",
Circle(var center, var r) => $"圆心 ({center.X}, {center.Y}),半径 {r}",
Rectangle(var w, var h) when w == h => $"正方形 {w}x{h}",
Rectangle(var w, var h) => $"矩形 {w}x{h}",
};
// 自定义解构
public class User {
public string Name { get; set; }
public int Age { get; set; }
public void Deconstruct(out string name, out int age) {
name = Name;
age = Age;
}
}
// 使用位置模式
if (user is ("张三", var age)) {
Console.WriteLine($"张三的年龄: {age}");
}
列表模式(C# 11)
// 匹配数组/列表
int[] arr = { 1, 2, 3, 4, 5 };
if (arr is [1, 2, .., 5]) {
Console.WriteLine("以1,2开头,以5结尾");
}
if (arr is [var first, .., var last]) {
Console.WriteLine($"首: {first}, 尾: {last}");
}
if (arr is [_, _, var third, ..]) {
Console.WriteLine($"第三个元素: {third}");
}
// switch 中的列表模式
string Classify(int[] values) => values switch {
[] => "空数组",
[var single] => $"单元素: {single}",
[var a, var b] => $"两个元素: {a}, {b}",
[var first, .. var middle, var last] => $"首: {first}, 中: {middle.Length}个, 尾: {last}",
};
逻辑模式组合
// and, or, not 组合模式
bool IsValid(object obj) => obj is not null and (int or double or decimal);
string Classify(int n) => n switch {
< 0 => "负数",
0 => "零",
> 0 and < 10 => "个位正数",
>= 10 and < 100 => "两位正数",
>= 100 => "三位及以上正数",
};
// not 模式
if (value is not null and not "") {
Process(value);
}
常见场景
异常处理
// 使用模式匹配处理不同异常
catch (Exception ex) when (ex is HttpRequestException { StatusCode: System.Net.HttpStatusCode.NotFound }) {
return "资源未找到";
}
catch (Exception ex) when (ex is HttpRequestException { StatusCode: var code } && (int)code >= 500) {
return "服务器错误";
}
catch (Exception ex) when (ex is TimeoutException or OperationCanceledException) {
return "请求超时";
}
领域事件处理
// 使用模式匹配处理领域事件
string HandleEvent(OrderEvent evt) => evt switch {
OrderCreated { OrderId: var id, Amount: > 10000 } => $"大额订单创建: {id}",
OrderCreated { OrderId: var id } => $"订单创建: {id}",
OrderPaid { PaymentMethod: "credit_card" } => "信用卡支付完成",
OrderPaid { PaymentMethod: var method } => $"{method} 支付完成",
OrderShipped { TrackingNumber: var tracking } => $"已发货: {tracking}",
OrderCancelled { Reason: var reason } => $"已取消: {reason}",
};
注意事项
- 模式匹配的顺序很重要,更具体的模式应放在前面
- 弃元模式 _ 必须放在最后,否则会遮盖后续模式
- 属性模式中 null 检查是隐式的:
{ Prop: value }不会对 null 对象抛异常 - 列表模式在 C# 11 中引入,需要确保项目使用 C# 11+
- when 子句可以添加额外条件,但会降低模式匹配的穷举检查能力
- 模式匹配在编译时会被优化,不会产生额外运行时开销
进阶用法
自定义模式(C# 13+)
// 使用静态方法实现自定义模式
public static class IntPatterns {
// 自定义 Even 模式
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Even(int value) => value % 2 == 0;
}
// 使用
if (number is > 0 and IntPatterns.Even) {
Console.WriteLine("正偶数");
}
递归模式匹配
// 递归处理表达式树
int Eval(Expr expr) => expr switch {
Literal { Value: var v } => v,
Add { Left: var l, Right: var r } => Eval(l) + Eval(r),
Mul { Left: var l, Right: var r } => Eval(l) * Eval(r),
Neg { Operand: var o } => -Eval(o),
};
abstract record Expr;
record Literal(int Value) : Expr;
record Add(Expr Left, Expr Right) : Expr;
record Mul(Expr Left, Expr Right) : Expr;
record Neg(Expr Operand) : Expr;