C# switch case 的极限教程
C# 的“模式匹配(Pattern Matching)”指的是:用更声明式的语法在运行时检查对象/值的形状(类型、属性、结构、范围等),并在匹配成功时提取变量。从 C# 7 开始引入,C# 8/9/10/11/12 逐步增强。下面按“常用—进阶”给你一套速查示例。
1) is 类型模式(Type pattern)+ 变量捕获
if (o is not string) { // 不是 string }
也可用于否定:
object o = "hello"; if (o is string s) { Console.WriteLine(s.Length); // s 已经是 string }
2) switch 表达式(推荐)与 when 守卫
static string Describe(object? x) => x switch { null => "null", int n when n < 0 => "negative int", int n => $"int {n}", string s => $"string len={s.Length}", _ => "other" };
3) 属性模式(Property pattern)
按对象属性“形状”匹配,非常适合 DTO/领域对象判断。
record Person(string Name, int Age); static string Category(Person p) => p switch { { Age: < 18 } => "minor", { Age: >= 65 } => "senior", { Name: "Alice", Age: >= 18 } => "special adult", _ => "adult" };
{ Age: < 18 } 里 < 18 本身也是一个“关系模式”。
4) 关系模式(Relational pattern)+ 逻辑模式(and/or/not)
static string ScoreLevel(int score) => score switch { < 0 or > 100 => "invalid", >= 90 and <= 100 => "A", >= 80 => "B", >= 60 => "C", _ => "F" };
5) 位置模式(Positional pattern):解构匹配
对 tuple 或支持 Deconstruct 的类型进行匹配。
static string Quadrant((int x, int y) p) => p switch { (0, 0) => "origin", (> 0, > 0) => "Q1", (< 0, > 0) => "Q2", _ => "other" };
自定义类型只要提供 Deconstruct:
class Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); public void Deconstruct(out int x, out int y) => (x, y) = (X, Y); } static bool IsOnDiagonal(Point p) => p is (var x, var y) && x == y;
6) 列表模式(List pattern,C# 11):匹配数组/Span/列表“形状”
static string HeadTail(int[] xs) => xs switch { [] => "empty", [var only] => $"one: {only}", [var head, .. var rest] => $"head={head}, restLen={rest.Length}", _ => "other" };
7) var/弃元 _ 模式:只关心部分
if (o is string _) { /* 只是确认是 string,不需要变量 */ } var msg = o switch { string { Length: var len } => $"len={len}", _ => "n/a" };
常见使用建议(实践向)
- 用
switch表达式替代大量if/else,更直观、也更不容易漏分支。 - 在分支中尽量使用属性/关系/列表模式,减少手写校验代码。
- 复杂条件时优先用
and/or/not+when做“守卫”,可读性最好。
如果你告诉我:
- 你的 C# 版本(例如 .NET 6 / .NET 8,对应 C# 10/12 左右),以及
- 你想匹配的对象结构(类/record/JSON DTO/数组等,贴一小段类型定义也行),
我可以按你的真实场景写一个更贴合的模式匹配写法(含可读性更好的分支组织方式)。
public enum Kind { Unknown, ExactFoo, StartsWithGet, EndsWithJson, ContainsAdmin, EqualsEmpty } public static Kind Classify(string? s) { switch (s) { case null: return Kind.Unknown; case "foo": return Kind.ExactFoo; // = 什么(完全相等) case "": return Kind.EqualsEmpty; // = 空字符串 case string x when x.StartsWith("get:", StringComparison.OrdinalIgnoreCase): return Kind.StartsWithGet; // 以什么打头 case string x when x.EndsWith(".json", StringComparison.OrdinalIgnoreCase): return Kind.EndsWithJson; // 以什么结尾 case string x when x.Contains("admin", StringComparison.OrdinalIgnoreCase): return Kind.ContainsAdmin; // 包含什么(.NET 5+ 支持 Contains + StringComparison) default: return Kind.Unknown; } }
record Person(string Name, int Age); static string Category(Person p) => p switch { { Age: < 18 } => "minor", { Age: >= 65 } => "senior", // 新增:Name 以 "maliang" 打头,且 Age 在 35/36/37/38/45/55 或 >= 100 { Name: var n, Age: 35 or 36 or 37 or 38 or 45 or 55 or >= 100 } when n.StartsWith("maliang", StringComparison.OrdinalIgnoreCase) => "maliang special", { Name: "Alice", Age: >= 18 } => "special adult", _ => "adult" };
漫思
浙公网安备 33010602011771号