C#模式匹配有哪些及怎么实现(云服务器、云主机、高防IP、高防服务器、香港服务器、美国服务器,开发技术)

时间:2024-05-08 09:37:55 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

    C%23%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8F%8A%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0

要使用模式匹配,首先要了解什么是模式。在使用正则表达式匹配字符串时,正则表达式自己就是一个模式,而对字符串使用这段正则表达式进行匹配的过程就是模式匹配。而在代码中也是同样的,我们对对象采用某种模式进行匹配的过程就是模式匹配。

C# 11 支持的模式有很多,包含:

声明模式(declaration pattern)

类型模式(type pattern)

常数模式(constant pattern)

关系模式(relational pattern)

逻辑模式(logical pattern)

属性模式(property pattern)

位置模式(positional pattern)

var 模式(var pattern)

丢弃模式(discard pattern)

列表模式(list pattern)

切片模式(slice pattern)

而其中,不少模式都支持递归,也就意味着可以模式嵌套模式,以此来实现更加强大的匹配功能。

模式匹配可以通过 switch 表达式来使用,也可以在普通的 switch 语句中作为 case 使用,还可以在 if 条件中通过 is 来使用。本文主要在 switch 表达式中使用模式匹配。

那么接下来就对这些模式进行介绍。

为了更直观地介绍模式匹配,我们接下来利用模式匹配来编写一个表达式计算器。

为了编写表达式计算器,首先我们需要对表达式进行抽象:

我们用上面这个 Expr<T> 来表示一个表达式,其中 T 是操作数的类型,然后进一步将表达式分为常数表达式 ConstantExpr、参数表达式 ParameterExpr、一元表达式 UnaryExpr、二元表达式 BinaryExpr 和三元表达式 TernaryExpr。最后提供一个 Eval 方法,用来计算表达式的值,该方法可以传入一个 args 来提供表达式计算所需要的参数。

有了一、二元表达式自然也需要运算符,例如加减乘除等,我们也同时定义 Operator 来表示运算符:

然后设置允许的运算符,其中前三个是一元运算符,后面的是二元运算符:

你可以能会好奇对 T 的运算能如何实现逻辑与或非,关于这一点,我们直接使用 0 来代表 false,非 0 代表 true

接下来就是分别实现各类表达式的时间!

常数表达式很简单,它保存一个常数值,因此只需要在构造方法中将用户提供的值存储下来。它的 Eval 实现也只需要简单返回存储的值即可:

参数表达式用来定义表达式计算过程中的参数,允许用户在对表达式执行 Eval 计算结果的时候传参,因此只需要存储参数名。它的 Eval 实现需要根据参数名在 args 中找出对应的参数值:

模式匹配会从上往下依次进行匹配,直到匹配成功为止。

上面的代码中你可能会好奇 [var (name, value), .. var tail] 是个什么模式,这个模式整体看是列表模式,并且列表模式内组合使用声明模式、位置模式和切片模式。例如:

[]:匹配一个空列表。[1, _, 3]:匹配一个长度是 3,并且首尾元素分别是 1、3 的列表。其中 _ 是丢弃模式,表示任意元素。

[_, .., 3]:匹配一个末元素是 3,并且 3 不是首元素的列表。其中 .. 是切片模式,表示任意切片。

[1, ..var tail]:匹配一个首元素是 1 的列表,并且将除了首元素之外元素的切片赋值给 tail。其中 var tailvar 模式,用于将匹配结果赋值给变量。

[var head, ..var tail]:匹配一个列表,将它第一个元素赋值给 head,剩下元素的切片赋值给 tail,这个切片里可以没有元素。

[var (name, value), ..var tail]:匹配一个列表,将它第一个元素赋值给 (name, value),剩下元素的切片赋值给 tail,这个切片里可以没有元素。其中 (name, value) 是位置模式,用于将第一个元素的解构结果根据位置分别赋值给 namevalue,也可以写成 (var name, var value)

一元表达式用来处理只有一个操作数的计算,例如非、取反等。

上面的代码中,首先利用了 C# 元组可作为左值的特性,分别使用一行代码就做完了构造方法和解构方法的赋值:(Op, Expr) = (op, expr)(op, expr) = (Op, Expr)。如果你好奇能否利用这个特性交换多个变量,答案是可以!

Eval 中,首先将类型模式、位置模式和声明模式组合成 UnaryOperator(var op),表示匹配 UnaryOperator 类型、并且能解构出一个元素的东西,如果匹配则将解构出来的那个元素赋值给 op

然后我们接着对解构出来的 op 进行匹配,这里用到了常数模式,例如 Operators.Inv 用来匹配 op 是否是 Operators.Inv。常数模式可以使用各种常数对对象进行匹配。

这里的 > Operators.LogicalNot< 0 则是关系模式,分别用于匹配大于 Operators.LogicalNot 的值和小于 0 的指。然后利用逻辑模式 or 将两个模式组合起来表示或的关系。逻辑模式除了 or 之外还有 andnot

由于我们在上面穷举了枚举中所有的一元运算符,因此也可以将 > Operators.LogicalNot or < 0 换成丢弃模式 _ 或者 var 模式 var foo,两者都用来匹配任意的东西,只不过前者匹配到后直接丢弃,而后者声明了个变量 foo 将匹配到的值放到里面:

二元表达式用来表示操作数有两个的表达式。有了一元表达式的编写经验,二元表达式如法炮制即可。

同理,也可以将 < Operators.Add or > Operators.LogicalOr 换成丢弃模式或者 var 模式。

三元表达式包含三个操作数:条件表达式 Cond、为真的表达式 Left、为假的表达式 Right。该表达式中会根据 Cond 是否为真来选择取 Left 还是 Right,实现起来较为简单:

完成。我们用了仅仅几十行代码就完成了全部的核心逻辑!这便是模式匹配的强大之处:简洁、直观且高效。

至此为止,我们已经完成了所有的表达式构造、解构和计算的实现。接下来我们为每一个表达式实现判等逻辑,即判断两个表达式(字面上)是否相同。

例如 a == b ? 2 : 4a == b ? 2 : 5 不相同,a == b ? 2 : 4c == d ? 2 : 4 不相同,而 a == b ? 2 : 4a == b ? 2 : 4 相同。

为了实现该功能,我们重写每一个表达式的 EqualsGetHashCode 方法。

常数表达式判等只需要判断常数值是否相等即可:

参数表达式判等只需要判断参数名是否相等即可:

一元表达式判等,需要判断被比较的表达式是否是一元表达式,如果也是的话则判断运算符和操作数是否相等:

上面的代码中用到了属性模式 { Operator: var op },用来匹配属性的值,这里直接组合了声明模式将属性 Operator 的值赋值给了 expr。另外,C# 中的元组可以组合起来进行判等操作,因此不需要写 op.Equals(Op.Operator) && expr.Equals(Expr),而是可以直接写 (op, expr).Equals((Op.Operator, Expr))

和一元表达式差不多,区别在于这次多了一个操作数:

和二元表达式差不多,只不过运算符 Op 变成了操作数 Cond

到此为止,我们为所有的表达式都实现了判等。

我们重载一些 Expr<T> 的运算符方便我们使用:

由于重载了 ==!=,编译器为了保险起见提示我们重写 EqualsGetHashCode,这里实际上并不需要重写,因此直接调用 base 上的方法保持默认行为即可。

然后编写两个扩展方法用来方便构造三元表达式,和从 Description 中获取运算符的名字:

由于有参数表达式参与时需要我们提前提供参数值才能调用 Eval 进行计算,因此我们写一个交互式的 Eval 来在计算过程中遇到参数表达式时提示用户输入值,起名叫做 InteractiveEval

这里在 GetArgs 方法中,模式 [var head, ..] 后面跟了一个 when head == name,这里的 when 用来给模式匹配指定额外的条件,仅当条件满足时才匹配成功,因此 [var head, ..] when head == name 的含义是,匹配至少含有一个元素的列表,并且将头元素赋值给 head,且仅当 head == name 时匹配才算成功。

最后我们再重写 ToString 方法方便输出表达式,就全部大功告成了。

接下来让我测试测试我们编写的表达式计算器:

运行后得到输出:

((((! ((((4) + (-3)) * ((4) - (-3))) > (x))) ? (y) : (z)) - (4)) > (x)) ? ((z) + (4)) : ((y) / (-3))

然后我们给 xyz 分别设置成 42、27 和 35,即可得到运算结果:

Parameter x: 42
Parameter y: 27
Parameter z: 35
-9

再测测表达式判等逻辑:

得到输出:

True
False

在未来,C# 将会引入活动模式,该模式允许用户自定义模式匹配的方法,例如:

上述代码定义了一个 T 的扩展方法 Even,用来匹配 value 是否为偶数,于是我们便可以这么使用:

此外,该模式还可以和解构模式结合,允许用户自定义解构行为,例如:

然后使用的时候:

即可对 x 这个字符串进行匹配,如果 x 可以被解析为 int,就取解析结果 result,否则取 0。

本文:C#模式匹配有哪些及怎么实现的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Java Web项目访问不到html文件怎么解决下一篇:

17 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18