经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
C#.Net筑基-模式匹配汇总
来源:cnblogs  作者:安木夕  时间:2024/5/15 16:51:38  对本文有异议

image.png

01、模式匹配概述

C#7开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了。

C# 支持多种模式,包括声明、类型、常量、关系、属性、列表、var 和弃元等,在isswitch语句、switch表达式中使用,还可以使用布尔逻辑关键字 andornot 组合多个模式,极大的简化了代码编写,可读性也还不错。

标题 说明 示例/备注
类型和声明模式 如果类型兼容,则申明并赋值变量 if (age is int i)
常量模式 检查表达式值是否等于、不等于(not)常量值 if(age is null || age is 0)
关系模式>< 使用关系运算符<><=>=匹配 case 0 or <=6
逻辑模式 not>and >or连接多个模式表达式 case < 12 and ( >6 or 6)
属性模式{:} 对实例的属性、字段进行模式匹配:{属性/字段:匹配模式} if (b is { Year: < 2000, Month: 1 or 11 })
位置模式(解构) 基于解构赋值进行模式匹配:(解构参数) if(point is (_,>0,>0))
var 模式 var申明(捕获)任意局部变量 if(point is var p && p.X>0)
弃元模式_ 弃元模式 _ 来匹配任何(其他)表达式 表示不要的
列表模式[] 对数组(列表)进行匹配,在中括号[]中匹配列表中的项 if(numbers is [_, 2, 3, ..])

?? 模式匹配基本都是语法糖,味道都不错!C#在编译时会输出原本的基础代码,可通过 https://sharplab.io/ 在线查看编译后的代码。


02、模式匹配

2.1、类型和声明模式

检查类型是否匹配,同时申明变量,如果类型兼容则申明并赋值变量,该变量在后面代码作用域中有效。

  1. object age = "123";
  2. if (age is int i) //类型匹配+申明变量i
  3. {
  4. Console.WriteLine($"age1 = {i}");
  5. }
  6. switch (age)
  7. {
  8. case string: //类型匹配
  9. Console.WriteLine($"type is string");
  10. break;
  11. case int iage: //类型匹配+申明变量iage
  12. Console.WriteLine($"age2 = {iage}");
  13. break;
  14. }
  15. //上面is语句编译后的代码效果:
  16. if (obj is int)
  17. {
  18. int value = (int)obj;
  19. }

2.2、常量模式

检查表达式值是否等于、不等于(not)常量值,常量值包括字面量常量,也包括const常量值。传统的Switch语句就是常量模式匹配。

  1. object age = null;
  2. if (age is not null && age is 100) //age is 100 等同于 age is int && (int)age==100
  3. {
  4. Console.WriteLine($"age1 = {age}");
  5. }
  6. var type = age switch{
  7. 1 or 2 or 3=>"婴儿",
  8. 4 => "幼儿",
  9. null or not 5 => "unknow",
  10. _=>"",
  11. };

2.3、关系模式><

用关系运算符来匹配表达式,就是对常量数值进行大小比较运算,使用关系运算符<><=>=,多个表达式可用andor连接,当然也支持括号。

  1. object age = 6;
  2. if (age is int n and >= 6)
  3. {
  4. Console.WriteLine("666");
  5. }
  6. switch (age)
  7. {
  8. case 0 or <=6:
  9. Console.WriteLine("幼儿");
  10. break;
  11. case < 12 and ( >6 or 6):
  12. Console.WriteLine("青少年");
  13. break;
  14. }

2.4、逻辑模式not/and/or

notandor 模式连结符来创建逻辑模式,连接多个模式表达式。

  • 优先级顺序:not>and >or
  • 推荐使用(括号)显示控制优先顺序,可读性更好。
  1. object age = 6;
  2. if (age is int n and (not 6 or >5) )
  3. {
  4. Console.WriteLine("666");
  5. }

2.5、属性模式{:}

对实例的属性、字段进行模式匹配,可以嵌套其他模式匹配,非常的强大,属性匹配用大括号来包装{属性/字段:匹配模式}

  • 多个属性/字段都匹配为true时,最终才会匹配成功。
  • 可以结合类型申明模式使用。
  • 可嵌套使用,会递归匹配。
  1. DateTime birthday = new DateTime(1999, 11, 12);
  2. if (birthday is { Year: < 2000, Month: 1 or 11 })
  3. {
  4. Console.WriteLine("年龄、星座不合适");
  5. }
  6. //嵌套使用
  7. public record Point(int X, int Y);
  8. public record Segment(Point Start, Point End);
  9. static bool IsAnyEndOnXAxis(Segment segment) =>
  10. segment is { Start: { Y: 0 } } or { End: { Y: 0 } };
  11. static bool IsAnyEndOnXAxis(Segment segment) =>
  12. segment is { Start.Y: 0 } or { End.Y: 0 };

2.6、位置模式(解构)

基于解构赋值进行模式匹配:

  • TuplerecordDictionaryEntry是内置支持解构的,关于解构赋值可参考相关内容。
  • 用括号()报装,这也是 解构(Deconstruct)的语法形式。
  • 可以嵌套其他模式匹配,如常量、关系、逻辑、属性模式等。
  1. void Main()
  2. {
  3. Point point = new Point("sam", 12, 13);
  4. var len = point switch
  5. {
  6. //类型匹配、属性模式、位置模式:Name属性必须为string,且长度为0,X、Y值为0
  7. (string { Length: <= 0 }, 0, 0) => 0,
  8. (_, > 0, 0) => point.X, //X值大于0,Y值为0
  9. (_, 0, > 0) => point.Y, //Y值大于0,X值为0
  10. (_, 10 or > 10, 10 or > 10) p => p.X * p.Y,
  11. _ => 0,
  12. };
  13. }
  14. public record Point(string Name, int X, int Y);

2.7、var 模式

var申明(捕获)任意局部变量,把表达式的结果分配给var临时变量。算是类型模式的变种,将类型名替换成了var

  1. void Main()
  2. {
  3. Point point = new Point("sam", 12, 13);
  4. if(point is var p && p.X>0 && p.Y>0){ //is var
  5. Console.WriteLine("OK");
  6. }
  7. var len = point switch
  8. {
  9. var (_,x,y) when x>0 && y>0 => true,// var
  10. };
  11. }
  12. public record Point(string Name, int X, int Y);

2.8、弃元模式_

弃元模式(Discard Pattern),字面理解就是被遗弃、没人要的。可以将弃元模式看做是一个占位符,表示一个没人用的变量,可匹配任意类型,用来简化代码。语法是用下划线“_”表示。

常用场景

  • 1、解构时的占位符。
  • 2、在Switch中匹配任意其他模式,类似default的作用。
  • 3、在out参数中占位,表示一个没人用的out参数。
  • 4、独立弃元,接收无用的表达式输出。
  1. var tuple = new Tuple<int, int>(3, 4);
  2. var (x, _) = tuple; //1、只需要第一个参数,其他就用“_”来占位
  3. Console.WriteLine(x); //3
  4. _= x switch
  5. {
  6. 2 or <2 => "small",
  7. int and <18=>"young",
  8. _=>"other", //2、匹配其他模式,效果同default
  9. };
  10. int.TryParse("", out _); //3、不用的out变量,实际上是申明了变量的
  11. async void Print(object arg)
  12. {
  13. _ = arg ?? throw new ArgumentException(); //4、接收无用的返回,效果同下
  14. if (arg == null) throw new ArgumentException();
  15. _ = Task.Run(()=>Console.WriteLine("task run")); //接收一个不用的返回
  16. }

弃元模式_是一个提供给编译器用的符号,告诉编译这个变量不用了,编译器会根据情况进行优化处理。在对out参数使用时,编译器会自动创建变量,如下代码:

  1. int.TryParse("",out _);
  2. //实际编译后的代码如下
  3. int result;
  4. int.TryParse("", out result);

image.png

??需要注意的是 下划线_是并不是一个关键字,也能当做参数名来使用,不要混用。

2.9、列表模式[]

C#11支持的,对数组(列表)进行匹配,在中括号[]中匹配列表中的项。

  • 跳过的项可以用弃元模式_
  • 可以用数组的切片模式匹配开头、结尾的元素。
  1. void Main()
  2. {
  3. int[] numbers = { 1, 2, 3, 4 };
  4. Console.WriteLine(numbers is [_, 2, 3, ..]); // True
  5. Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]); // False
  6. }

03、模式匹配应用

上面的各种模式匹配主要就用在 is 运算符switch 语句switch 表达式 中。

image.png

3.1、is运算符

is 运算符 本来主要是用来检测类型兼容性的,加上模式匹配就能玩出各种花样了,极大简化了让各种检查类的代码。

  1. object value = 12;
  2. if (value is int && value is not null) //is类型检测+逻辑模式
  3. {
  4. Console.WriteLine(value);
  5. }
  6. if (value is int a && a > 6) //+申明模式
  7. {
  8. Console.WriteLine(a);
  9. }
  10. if (value is int age and > 10 and < 14) //关系模式
  11. {
  12. Console.WriteLine(age);
  13. }
  14. var user = new { Name = "sam", Age = 12 };
  15. if (user is { Name: _, Age: > 10 }) //属性模式
  16. {
  17. Console.WriteLine(user.Name);
  18. }
  19. int[] arr = new int[] { 1, 2, 3 };
  20. if (arr is [> 0, ..]) //列表模式:第一个元素>0
  21. {
  22. Console.WriteLine(arr);
  23. }
  24. var dt = new Tuple<string, int>("sam", 100);
  25. if (dt is (_, > 60) d) //位置模式+申明模式(好像没什么用)
  26. {
  27. Console.WriteLine(d.Item1);
  28. }

3.2、switch..case语句

switch..case 语句 是很多语言中都有的基本多条件分支语句,传统的 case 只能用于匹配常量,多用于枚举。

  • case不能穿透,一个case 执行完后必须break结束,或者return返回(退出方法),可以多个case匹配执行一组逻辑代码。
  • 传统的case就是常量模式,而现代的case可以结合上面多种模式使用,非常强大。
  • when,自由附加更多条件。
  1. int age = 22;
  2. string sex = "Male";
  3. switch (age)
  4. {
  5. case 1:
  6. case 2:
  7. Console.WriteLine("婴儿");
  8. break;
  9. case <= 3:
  10. Console.WriteLine("幼儿");
  11. break;
  12. case > 10 and < 16:
  13. Console.WriteLine("青少年");
  14. break;
  15. case > 18 when sex == "Male":
  16. Console.WriteLine("成年男性");
  17. break;
  18. case int:
  19. break;
  20. }

3.3、switch表达式

C#8switch有了新的语法 —— switch 表达式 ,可以看做是switch..case语句的一个变种,使用比较类似。switch表达式是一个赋值(输出)语句。

  • =>左侧为模式(返回一个bool),如果模式匹配(true)则返回右侧的值,最后一个弃元模式匹配其他情况,同default效果。
  1. int type = 6;
  2. var message = type switch
  3. {
  4. <= 1 => "success",
  5. 2 => "warning",
  6. 3 => "error",
  7. > 3 and < 10 => "other error",
  8. _ => "unkonwn error",
  9. };

可以用when来进行更多的判断,when后面的表达式就很自由了,只要返回boo即可。

  1. object type = 6;
  2. var message = type switch
  3. {
  4. int i when i<6 => "ok",
  5. string s when s=="null"=>"Null",
  6. string s when !string.IsNullOrEmpty(s)=>"string value",
  7. _=>"unknown value"
  8. };
  9. Console.WriteLine(message);

支持多个变量的组合模式:用括号()包含多个变量

  1. string gender = "male";
  2. int age = 10;
  3. string type = (gender,age) switch{
  4. ("male",>18)=>"VIP",
  5. (not "male",>26 and <35)=>"VVIP",
  6. _=>"",
  7. };

参考资料


??版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀

原文链接:https://www.cnblogs.com/anding/p/18176056

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号