>> >> >> Reference << << << <<<<<<Ref>>>>>>
>> >> >> Indexer << << << <<<<<<Idx>>>>>>
Matched: 0

Tags

    Categories

      Types

        Top Results

          Pattern Matching
          M: 2025-06-01 - ljf12825

          模式

          模式就是一种匹配规则,用来判断对象的结构、类型或值,并可绑定变量
          核心思想:

          1. 匹配类型(is it s string? int? Person?)
          2. 匹配值或条件(大于0? 小于0? 某属性为true?)
          3. 绑定变量(匹配成功后把值赋给变量)
          4. 可组合(and/or/not)

          可以把模式理解为C#对象解构 + 条件判断 + 绑定语法糖的集合

          模式的几大类别

          C#从7.0到12逐步扩展了模式类型,主要有以下几种

          模式类型语法示例作用
          类型模式(Type Pattern)x is int n判断类型,并绑定变量
          常量模式(Constant Pattern)x is 0判断值等于某常量
          关系模式(Relational Pattern)x is > 0大小比较
          逻辑模式(Logical Pattern)x is > 0 and < 10组合多个模式(and/or/not)
          否定模式(Negation Pattern)x is not null对模式取反
          属性模式(Property Pattern)p is { X: 0, Y: 0 }匹配对象的属性结构
          位置模式(Positional Pattern)Point(var x, var y)对对象或record解构
          递归模式p is { Name: "Alice", Age: > 0 }可以嵌套匹配对象内部结构

          模式匹配

          模式匹配是对对象结构和类型的“解构+判断+绑定”操作
          简单来说,它让ifswitchis不再只是“类型判断”,而是能直接在匹配时提取数据、判断条件

          解决的痛点

          is类型模式

          C#7.0之后可以这样写

          object obj = 123;
          
          if (obj is int n) Console.WriteLine($"是整数,值为{n}");
          

          这相当于

          if (obj is int)
          {
              int n = (int)obj;
              Console.WriteLine(n);
          }
          

          区别:

          • 新写法里is直接完成类型检查 + 类型转换 + 变量绑定
          • n只在if的真分支内可见

          类型模式 + 守卫条件(when

          object obj = 42;
          
          if (obj is int n && n > 0)
              Console.WriteLine("正整数");
          
          // 或者在swtich里写
          switch (obj)
          {
              case int n when n > 0:
                  Console.WriteLine("正数");
                  break;
              case int n:
                  Console.WriteLine("非正数");
                  break;
          }
          

          常见模式类型

          C#模式匹配的核心在于这些“模式类型”

          类型模式(Type Pattern)

          x is int n 作用:判断类型并绑定变量

          常量模式(Constant Pattern)

          if (x is 0)
              Console.WriteLine("零");
          

          判断对象是否等于某个常量

          关系模式(Relational Pattern)

          if (x is > 0)
              Console.WriteLine("正数");
          else if (x is < 0)
              Console.WriteLine("负数");
          

          在C#9.0引入,用<, >, <=, >=做比较

          逻辑模式(Logical Pattern)

          可以组合多个模式

          if (x is >= 0 and <= 100)
              Console.WriteLine("在 0~100之间");
          if (x is < 0 or > 100)
              Console.WriteLine("超出范围");
          

          否定模式(Negation Pattern)

          if (obj is not null)
              Console.WriteLine("不是 null");
          

          位置模式(Positional Pattern)

          假设有一个结构体

          public record Point(int X, int Y);
          

          可以直接匹配并结构它

          Point p = new(3, 4);
          
          if (p is Point(var x, var y))
              Console.WriteLine($"x={x}, y={y}");
          if (p is Point(0, _))
              Console.WriteLine("在Y轴上");
          

          属性模式(Property Pattern)

          可以直接匹配对象的属性结构

          if (p is { X: 0, Y: 0 })
              Console.WriteLine("原点");
          if (p is { X: > 0, Y: > 0})
              Console.WriteLine("第一象限");
          

          组合使用

          所有模式都可以嵌套组合

          if (p is { X: > 0 and < 10, Y: > 0})
              Console.WriteLine("右上角小区域");
          

          switch表达式(C# 8+)

          现代C#最强的模式匹配表达式

          string Describe(Point p) => p switch
          {
              { X: 0, Y: 0 } => "原点",
              { X: > 0, Y: > 0 } => "第一象限",
              { X: < 0, Y: > 0 } => "第二象限",
              _ => "其他位置"
          };
          

          特点:

          • switch可以直接作为表达式返回值
          • 不需要写break
          • 匹配顺序自上而下
          • _表示默认模式

          底层原理

          模式匹配的本质:语法糖 + 编译器静态分析 + IL分支重写
          C#的模式匹配——无论是is, switch,还是各种property/tuple/relational pattern——底层都不是运行时魔法,而是:
          编译器再编译期,把高层模式匹配语法“翻译”成if/else、类型检查、结构调用、常量比较、IL分支指令等基础结构
          所有模式匹配在运行时没有额外runtime支撑,它只靠CLR原有能力:类型检查、常量比较、虚方法调用、IL分支;是纯语法层能力

          基础原理

          编译器生成的IL做了以下几个步骤

          1. 类型模式(type pattern)
          if (obj is string s) {}
          

          编译成IL的核心是

          • isinst指令:把对象尝试cast成某类型
          • brtrue.s/brfalse.s:是否null判断

          IL大概类似

          ldloc obj
          isinst [System.String]
          stloc s
          ldloc s
          brtrue label_matched 
          

          也就是说:编译器帮你做类型检查和赋值,没有任何runtime pattern 引擎

          1. 常量模式(constant pattern)
          if (x is 3) {}
          

          编译成

          ldloc x
          ldc.i4 3
          beq label_matched 
          

          本质就是一个整数比较,没有额外开销

          1. Relational Pattern (关系模式:< > <= >=
          if (x is > 10)
          

          编译器会把它拆成

          ldloc x
          ldc.i4 10
          cat (or clt)
          brtrue ...
          

          本质就是比较指令

          1. 属性模式(Property Pattern)
          if (p is { X: > 10, Y: 3})
          

          编译器做的事情是

          • 先检查类型(如果是引用类型就implicit做isinst)
          • 调用属性getter:callvirt get_X, callvirt get_Y
          • 用上面哪些pattern(constant, relational)继续判断

          没有额外消耗,本质是

          if (p != null && p.X > 10 && p.Y == 3)
          

          这是完全等价的

          1. 元组模式(Tuple Pattern)
          if ((x, y) is (>0, <0))
          

          编译器展开成

          if (x > 0 && y < 0)
          

          IL只是多个比较与逻辑与的组合

          switch模式匹配底层:决策树优化(Decision DAG)

          这是模式匹配最核心的底层部分,C#编译器针对switch语句创建了一套优化决策树,让匹配尽可能少走分支

          switch (shape)
          {
              case Circle { Radius: > 10 }: ...
              case Circle { Radius: <= 10 }: ...
              case Rectangle { Width: > 0, Height: > 0 }: ...
          }
          

          编译器不会按上面写的顺序逐行if/else,而是

          1. 先分析shape的动态类型
          2. 同类型的case合并
          3. 进行属性访问排序
          4. 用DGA优化重复条件

          最终生成一套最短路径的IL分支逻辑(落到isinst/getter/比较指令上)

          这提升性能,没有运行时消耗,也不是解析式,而是编译期优化成“最佳分支路径”
          微软Roslyn团队的论文称之为:Decision DAG(决策有向无环图)

          可以理解为:编译期把你的模式匹配表达式转换成一个智能的、最优的、最短路径的if-else树

          总结

          模式匹配不会产生运行时反射、不会有动态dispatch,所有复杂的逻辑都在编译期完成

          • 分析类型层次
          • 分析常量
          • 分析pattern结构
          • 生成决策树
          • 重写IL

          运行时就是执行普通代码

          CLR层面没有 pattern matching opcode,没有类似match, patterncase_pattern的IL指令;所有模式匹配最终落在这几个IL指令上

          • isinst(类型判断)
          • brtrue/brfalse(条件分支)
          • beq/bge/blt(数值比较)
          • callvirt(属性访问)
          • ldc.i4(加载常量)
          • ldobj(加载字段)

          模式匹配的复杂性都在C#编译器,而非.NET runtime或IL层

          示例

          1. 基本类型模式匹配
          // is 表达式模式
          public static void CheckType(object obj)
          {
              if (obj is int number)
              {
                  Console.WriteLine($"这是一个整数: {number}");
              }
              else if (obj is string text)
              {
                  Console.WriteLine($"这是一个字符串: {text}");
              }
              else if (obj is null)
              {
                  Console.WriteLine("这是空值");
              }
          }
          
          // switch 表达式
          public static string GetTypeDescription(object obj) => obj switch
          {
              int i => $"整数: {i}",
              string s => $"字符串: {s}",
              double d => $"双精度浮点数: {d}",
              null => "空值",
              _ => "未知类型"
          };
          
          1. 属性模式匹配
          public class Person
          {
              public string Name { get; set; }
              public int Age { get; set; }
              public string City { get; set; }
          }
          
          public static string GetPersonCategory(Person person) => person switch
          {
              { Age: < 18 } => "未成年人",
              { Age: >= 18 and < 65 } => "成年人",
              { Age: >= 65 } => "老年人",
              _ => "未知"
          };
          
          // 多个属性组合匹配
          public static string GetPersonInfo(Person person) => person switch
          {
              { Age: >= 18, City: "北京" } => "北京成年人",
              { Age: < 18, City: "上海" } => "上海未成年人",
              { Name: var name, Age: > 60 } => $"{name}是老年人",
              _ => "其他"
          };
          
          1. 元组模式
          public static string RockPaperScissors(string first, string second) 
              => (first, second) switch
              {
                  ("rock", "scissors") => "rock 赢了",
                  ("rock", "paper") => "paper 赢了",
                  ("paper", "rock") => "paper 赢了",
                  ("paper", "scissors") => "scissors 赢了",
                  ("scissors", "rock") => "rock 赢了",
                  ("scissors", "paper") => "scissors 赢了",
                  (_, _) when first == second => "平局",
                  _ => "输入无效"
              };
          
          1. 位置模式匹配(解构模式)
          public readonly struct 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);
          }
          
          public static string GetQuadrant(Point point) => point switch
          {
              (0, 0) => "原点",
              var (x, y) when x > 0 && y > 0 => "第一象限",
              var (x, y) when x < 0 && y > 0 => "第二象限",
              var (x, y) when x < 0 && y < 0 => "第三象限",
              var (x, y) when x > 0 && y < 0 => "第四象限",
              _ => "在坐标轴上"
          };
          
          1. 递归模式匹配
          public abstract class Shape { }
          public class Circle : Shape { public double Radius { get; set; } }
          public class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } }
          public class Triangle : Shape { public double Base { get; set; } public double Height { get; set; } }
          
          public static double CalculateArea(Shape shape) => shape switch
          {
              Circle { Radius: var r } => Math.PI * r * r,
              Rectangle { Width: var w, Height: var h } => w * h,
              Triangle { Base: var b, Height: var h } => 0.5 * b * h,
              _ => throw new ArgumentException("未知形状")
          };
          
          1. 列表模式匹配(C# 11+)
          public static string CheckList(int[] numbers) => numbers switch
          {
              [] => "空数组",
              [1] => "只有一个元素1",
              [1, 2] => "包含1和2",
              [1, 2, .. var rest] => $"以1,2开头,剩余{rest.Length}个元素",
              [var first, .., var last] => $"第一个元素是{first},最后一个元素是{last}",
              _ => "其他数组"
          };
          
          // 使用示例
          Console.WriteLine(CheckList(new int[] { }));           // 空数组
          Console.WriteLine(CheckList(new int[] { 1 }));         // 只有一个元素1
          Console.WriteLine(CheckList(new int[] { 1, 2 }));      // 包含1和2
          Console.WriteLine(CheckList(new int[] { 1, 2, 3, 4 }));// 以1,2开头,剩余2个元素
          
          1. 关系模式匹配
          public static string GetTemperatureDescription(double temp) => temp switch
          {
              < -10 => "极寒",
              >= -10 and < 0 => "寒冷",
              >= 0 and < 15 => "凉爽",
              >= 15 and < 25 => "舒适",
              >= 25 and < 35 => "温暖",
              >= 35 => "炎热"
          };
          
          public static string GetGrade(int score) => score switch
          {
              >= 90 and <= 100 => "优秀",
              >= 80 and < 90 => "良好",
              >= 70 and < 80 => "中等",
              >= 60 and < 70 => "及格",
              >= 0 and < 60 => "不及格",
              _ => "无效分数"
          };