云香丽 發表於 2024-5-14 08:00:00

C#.Net筑基-模式匹配汇总

<p><img src="https://img2024.cnblogs.com/blog/151257/202405/151257-20240506221243478-1961323861.png" alt="image.png" loading="lazy"></p>
<h1 id="01模式匹配概述">01、模式匹配概述</h1>
<p>从<code>C#7</code>开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了。</p>
<p>C# 支持多种模式,包括声明、类型、常量、关系、属性、列表、var 和弃元等,在<code>is</code>、<code>switch</code>语句、<code>switch</code>表达式中使用,还可以使用布尔逻辑关键字 <code>and</code>、<code>or</code> 和 <code>not</code> 组合多个模式,极大的简化了代码编写,可读性也还不错。</p>
<table>
<thead>
<tr>
<th><strong>标题</strong></th>
<th><strong>说明</strong></th>
<th><strong>示例/备注</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>类型和声明模式</td>
<td>如果类型兼容,则申明并赋值变量</td>
<td><code>if (age is int i)</code></td>
</tr>
<tr>
<td>常量模式</td>
<td>检查表达式值是否等于、不等于(not)常量值</td>
<td><code>if(age is null &amp;#124;&amp;#124; age is 0)</code></td>
</tr>
<tr>
<td>关系模式<code>&gt;&lt;</code></td>
<td>使用关系运算符<code>&lt;</code>、<code>&gt;</code>、<code>&lt;=</code> 或 <code>&gt;=</code>匹配</td>
<td><code>case 0 or &lt;=6</code></td>
</tr>
<tr>
<td>逻辑模式</td>
<td>用<code>not</code>&gt;<code>and</code> &gt;<code>or</code>连接多个模式表达式</td>
<td><code>case &lt; 12 and ( &gt;6 or 6)</code></td>
</tr>
<tr>
<td>属性模式<code>{:}</code></td>
<td>对实例的属性、字段进行模式匹配:<code>{属性/字段:匹配模式}</code></td>
<td><code>if (b is { Year: &lt; 2000, Month: 1 or 11 })</code></td>
</tr>
<tr>
<td>位置模式(解构)</td>
<td>基于解构赋值进行模式匹配:<code>(解构参数)</code></td>
<td><code>if(point is (_,&gt;0,&gt;0))</code></td>
</tr>
<tr>
<td>var 模式</td>
<td>用<code>var</code>申明(捕获)任意局部变量</td>
<td><code>if(point is var p &amp;&amp; p.X&gt;0)</code></td>
</tr>
<tr>
<td>弃元模式_</td>
<td>弃元模式 <code>_ </code>来匹配任何(其他)表达式</td>
<td>表示不要的</td>
</tr>
<tr>
<td>列表模式[]</td>
<td>对数组(列表)进行匹配,在中括号<code>[]</code>中匹配列表中的项</td>
<td><code>if(numbers is )</code></td>
</tr>
</tbody>
</table>
<p><img src="https://img2024.cnblogs.com/blog/151257/202405/151257-20240506221243341-1981014503.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>📢 模式匹配基本都是语法糖,味道都不错!C#在编译时会输出原本的基础代码,可通过 https://sharplab.io/ 在线查看编译后的代码。</p>
</blockquote>
<hr>
<h1 id="02模式匹配">02、模式匹配</h1>
<h2 id="21类型和声明模式">2.1、类型和声明模式</h2>
<p>检查类型是否匹配,同时申明变量,如果类型兼容则申明并赋值变量,该变量在后面代码作用域中有效。</p>
<pre><code class="language-csharp">object age = "123";
if (age is int i)//类型匹配+申明变量i
{
    Console.WriteLine($"age1 = {i}");
}
switch (age)
{
    case string:   //类型匹配
      Console.WriteLine($"type is string");
      break;
    case int iage: //类型匹配+申明变量iage
      Console.WriteLine($"age2 = {iage}");
      break;
}

//上面is语句编译后的代码效果:
if (obj is int)
{
    int value = (int)obj;
}
</code></pre>
<h2 id="22常量模式">2.2、常量模式</h2>
<p>检查表达式值是否等于、不等于(<code>not</code>)常量值,常量值包括字面量常量,也包括<code>const</code>常量值。传统的<code>Switch</code>语句就是常量模式匹配。</p>
<pre><code class="language-csharp">object age = null;
if (age is not null&amp;&amp; age is 100)   //age is 100 等同于 age is int &amp;&amp; (int)age==100
{
    Console.WriteLine($"age1 = {age}");
}
var type = age switch{
      1 or 2 or 3=&gt;"婴儿",
      4 =&gt; "幼儿",
      null or not 5 =&gt; "unknow",
      _=&gt;"",
};
</code></pre>
<h2 id="23关系模式">2.3、关系模式<code>&gt;&lt;</code></h2>
<p>用关系运算符来匹配表达式,就是对常量数值进行大小比较运算,使用关系运算符<code>&lt;</code>、<code>&gt;</code>、<code>&lt;=</code> 或 <code>&gt;=</code>,多个表达式可用<code>and</code>、<code>or</code>连接,当然也支持括号。</p>
<pre><code class="language-csharp">object age = 6;
if (age is int n and &gt;= 6)
{
    Console.WriteLine("666");
}
switch (age)
{
    case 0 or &lt;=6:
      Console.WriteLine("幼儿");
      break;
    case &lt; 12 and ( &gt;6 or 6):
      Console.WriteLine("青少年");
      break;
}
</code></pre>
<h2 id="24逻辑模式notandor">2.4、逻辑模式<code>not</code>/<code>and</code>/<code>or</code></h2>
<p>用 <code>not</code>、<code>and</code> 和 <code>or</code> 模式连结符来创建逻辑模式,连接多个模式表达式。</p>
<ul>
<li>优先级顺序:<code>not</code>&gt;<code>and</code> &gt;<code>or</code>。</li>
<li>推荐使用<code>(括号)</code>显示控制优先顺序,可读性更好。</li>
</ul>
<pre><code class="language-csharp">object age = 6;
if (age is int n and (not 6 or &gt;5) )
{
    Console.WriteLine("666");
}
</code></pre>
<h2 id="25属性模式">2.5、属性模式<code>{:}</code></h2>
<p>对实例的属性、字段进行模式匹配,可以嵌套其他模式匹配,非常的强大,属性匹配用大括号来包装<code>{属性/字段:匹配模式}</code>。</p>
<ul>
<li>多个属性/字段都匹配为<code>true</code>时,最终才会匹配成功。</li>
<li>可以结合类型申明模式使用。</li>
<li>可嵌套使用,会递归匹配。</li>
</ul>
<pre><code class="language-csharp">DateTime birthday = new DateTime(1999, 11, 12);
if (birthday is { Year: &lt; 2000, Month: 1 or 11 })
{
    Console.WriteLine("年龄、星座不合适");
}

//嵌套使用
public record Point(int X, int Y);
public record Segment(Point Start, Point End);

static bool IsAnyEndOnXAxis(Segment segment) =&gt;
    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };
static bool IsAnyEndOnXAxis(Segment segment) =&gt;
    segment is { Start.Y: 0 } or { End.Y: 0 };
</code></pre>
<h2 id="26位置模式解构">2.6、位置模式(解构)</h2>
<p>基于解构赋值进行模式匹配:</p>
<ul>
<li><code>Tuple</code>、record 和 DictionaryEntry是内置支持解构的,关于解构赋值可参考相关内容。</li>
<li>用括号<code>()</code>报装,这也是 解构(Deconstruct)的语法形式。</li>
<li>可以嵌套其他模式匹配,如常量、关系、逻辑、属性模式等。</li>
</ul>
<pre><code class="language-csharp">void Main()
{
        Point point = new Point("sam", 12, 13);
        var len = point switch
        {
                //类型匹配、属性模式、位置模式:Name属性必须为string,且长度为0,X、Y值为0
                (string { Length: &lt;= 0 }, 0, 0) =&gt; 0,               
                (_, &gt; 0, 0) =&gt; point.X,//X值大于0,Y值为0
                (_, 0, &gt; 0) =&gt; point.Y,//Y值大于0,X值为0
                (_, 10 or &gt; 10, 10 or &gt; 10) p =&gt; p.X * p.Y,
                _ =&gt; 0,
        };
}
public record Point(string Name, int X, int Y);
</code></pre>
<h2 id="27var-模式">2.7、var 模式</h2>
<p>用<code>var</code>申明(捕获)任意局部变量,把表达式的结果分配给<code>var</code>临时变量。算是类型模式的变种,将类型名替换成了<code>var</code>。</p>
<pre><code class="language-csharp">void Main()
{
        Point point = new Point("sam", 12, 13);
        if(point is var p &amp;&amp; p.X&gt;0 &amp;&amp; p.Y&gt;0){   //is var
                Console.WriteLine("OK");
        }       
        var len = point switch
        {
                var (_,x,y) when x&gt;0 &amp;&amp; y&gt;0 =&gt; true,// var
        };
}
public record Point(string Name, int X, int Y);
</code></pre>
<h2 id="28弃元模式_">2.8、弃元模式_</h2>
<p>弃元模式(Discard Pattern),字面理解就是被遗弃、没人要的。可以将弃元模式看做是一个占位符,表示一个没人用的变量,可匹配任意类型,用来<strong>简化代码</strong>。语法是用下划线“<code>_</code>”表示。</p>
<p><strong>常用场景</strong>:</p>
<ul>
<li>1、解构时的占位符。</li>
<li>2、在<code>Switch</code>中匹配任意其他模式,类似<code>default</code>的作用。</li>
<li>3、在<code>out</code>参数中占位,表示一个没人用的<code>out</code>参数。</li>
<li>4、独立弃元,接收无用的表达式输出。</li>
</ul>
<pre><code class="language-csharp">var tuple = new Tuple&lt;int, int&gt;(3, 4);
var (x, _) = tuple;   //1、只需要第一个参数,其他就用“_”来占位
Console.WriteLine(x); //3

_= x switch
{
    2 or &lt;2 =&gt; "small",
    int and &lt;18=&gt;"young",
    _=&gt;"other",//2、匹配其他模式,效果同default
};

int.TryParse("", out _); //3、不用的out变量,实际上是申明了变量的

async void Print(object arg)
{
    _ = arg ?? throw new ArgumentException();//4、接收无用的返回,效果同下
    if (arg == null) throw new ArgumentException();
    _ = Task.Run(()=&gt;Console.WriteLine("task run")); //接收一个不用的返回
}
</code></pre>
<p>弃元模式<code>_</code>是一个提供给编译器用的符号,告诉编译这个变量不用了,编译器会根据情况进行优化处理。在对<code>out</code>参数使用时,编译器会自动创建变量,如下代码:</p>
<pre><code class="language-csharp">int.TryParse("",out _);
//实际编译后的代码如下
int result;
int.TryParse("", out result);
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/151257/202405/151257-20240506221243178-867366680.png" alt="image.png" loading="lazy"></p>
<blockquote>
<p>📢需要注意的是 下划线<code>_</code>是并不是一个关键字,也能当做参数名来使用,不要混用。</p>
</blockquote>
<h2 id="29列表模式">2.9、列表模式[]</h2>
<p>C#11支持的,对数组(列表)进行匹配,在中括号<code>[]</code>中匹配列表中的项。</p>
<ul>
<li>跳过的项可以用弃元模式<code>_</code>。</li>
<li>可以用数组的切片模式匹配开头、结尾的元素。</li>
</ul>
<pre><code class="language-csharp">void Main()
{
        int[] numbers = { 1, 2, 3, 4 };

        Console.WriteLine(numbers is );      // True
        Console.WriteLine(numbers is ); // False
}
</code></pre>
<hr>
<h1 id="03模式匹配应用">03、模式匹配应用</h1>
<p>上面的各种模式匹配主要就用在 is 运算符、switch 语句、switch 表达式 中。</p>
<p><img src="https://img2024.cnblogs.com/blog/151257/202405/151257-20240506221243451-1953548790.png" alt="image.png" loading="lazy"></p>
<h2 id="31is运算符">3.1、is运算符</h2>
<p>is 运算符 本来主要是用来检测类型兼容性的,加上模式匹配就能玩出各种花样了,极大简化了让各种检查类的代码。</p>
<pre><code class="language-csharp">object value = 12;
if (value is int &amp;&amp; value is not null) //is类型检测+逻辑模式
{            
    Console.WriteLine(value);
}
if (value is int a &amp;&amp; a &gt; 6) //+申明模式
{
    Console.WriteLine(a);
}
if (value is int age and &gt; 10 and &lt; 14) //关系模式
{
    Console.WriteLine(age);
}
var user = new { Name = "sam", Age = 12 };
if (user is { Name: _, Age: &gt; 10 })    //属性模式
{
    Console.WriteLine(user.Name);
}
int[] arr = new int[] { 1, 2, 3 };
if (arr is [&gt; 0, ..])//列表模式:第一个元素&gt;0
{
    Console.WriteLine(arr);
}
var dt = new Tuple&lt;string, int&gt;("sam", 100);
if (dt is (_, &gt; 60) d) //位置模式+申明模式(好像没什么用)
{
    Console.WriteLine(d.Item1);
}

</code></pre>
<h2 id="32switchcase语句">3.2、switch..case语句</h2>
<p>switch..case 语句 是很多语言中都有的基本多条件分支语句,传统的 case 只能用于匹配常量,多用于枚举。</p>
<ul>
<li><code>case</code>不能穿透,一个<code>case</code> 执行完后必须<code>break</code>结束,或者<code>return</code>返回(退出方法),可以多个<code>case</code>匹配执行一组逻辑代码。</li>
<li>传统的<code>case</code>就是常量模式,而现代的<code>case</code>可以结合上面多种模式使用,非常强大。</li>
<li>加<code>when</code>,自由附加更多条件。</li>
</ul>
<pre><code class="language-csharp">        int age = 22;
        string sex = "Male";       
        switch (age)
        {
                case 1:
                case 2:
                        Console.WriteLine("婴儿");
                        break;
                case &lt;= 3:
                        Console.WriteLine("幼儿");
                        break;
                case &gt; 10 and &lt; 16:
                        Console.WriteLine("青少年");
                        break;
                case &gt; 18 when sex == "Male":
                        Console.WriteLine("成年男性");
                        break;
                case int:
                        break;
        }
</code></pre>
<h2 id="33switch表达式">3.3、switch表达式</h2>
<p><code>C#8</code>中<code>switch</code>有了新的语法 —— switch 表达式 ,可以看做是<code>switch..case</code>语句的一个变种,使用比较类似。<code>switch</code>表达式是一个赋值(输出)语句。</p>
<ul>
<li><code>=&gt;</code>左侧为模式(返回一个bool),如果模式匹配(true)则返回右侧的值,最后一个弃元模式匹配其他情况,同<code>default</code>效果。</li>
</ul>
<pre><code class="language-csharp">int type = 6;
var message = type switch
{
    &lt;= 1 =&gt; "success",
    2 =&gt; "warning",
    3 =&gt; "error",
    &gt; 3 and &lt; 10 =&gt; "other error",
    _ =&gt; "unkonwn error",
};
</code></pre>
<p>可以用<code>when</code>来进行更多的判断,<code>when</code>后面的表达式就很自由了,只要返回<code>boo</code>即可。</p>
<pre><code class="language-csharp">object type = 6;
var message = type switch
{
    int i when i&lt;6 =&gt; "ok",
    string s when s=="null"=&gt;"Null",
    string s when !string.IsNullOrEmpty(s)=&gt;"string value",
    _=&gt;"unknown value"
};
Console.WriteLine(message);
</code></pre>
<p>支持多个变量的组合模式:用括号<code>()</code>包含多个变量</p>
<pre><code class="language-csharp">string gender = "male";
int age = 10;
string type = (gender,age) switch{
    ("male",&gt;18)=&gt;"VIP",
    (not "male",&gt;26 and &lt;35)=&gt;"VVIP",
    _=&gt;"",
};
</code></pre>
<hr>
<h1 id="参考资料">参考资料</h1>
<ul>
<li>模式匹配 - 模式中的 is 和 switch 表达式,以及 and、or 和 not 运算符</li>
<li>析构元组和其他类型</li>
</ul>
<hr>
<blockquote>
<p><strong>©️版权申明</strong>:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!<em>原文编辑地址-语雀</em></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/anding/p/18176056
頁: [1]
查看完整版本: C#.Net筑基-模式匹配汇总