欢迎光临散文网 会员登陆 & 注册

C# 模式匹配的综合应用

2023-01-17 10:48 作者:SunnieShine  | 我要投稿

下面我们来讨论一些关于前面讲解的模式匹配的综合内容。

1、如何判断集合至少有一个元素

如下的这些模式都可以。

  • not (not { } or []):先看内层 not { } or [],它表示要么 not { },要么 []not { } 等于 is null,而 [] 等于 Length == 0 属性判断,所以对整个模式取反 not (not { } or []) 就是 not (null or { Length: 0 }),即不空且至少包含一个元素;

  • { Length: not 0 }:最基础的属性模式判断。不过这个用法稍微注意一下,如果集合不含 Length 属性而是 Count 属性,你可能需要改掉这里的 Length 名称;

  • not (null or [])not { } 就是 null,所以和第一个写法等价;

  • { Length: > 0 }:和第二个一致,只不过这里是要求 Length 必须非负。但是 C# 11 开始模模式必须非负,因此语义上 > 0not 0 是一致的了;

  • { } and not []{ }not nullnot [] 则是 Length == 0 的属性判断,因此结合起来就是序列不空,且至少有一个元素;

  • [.., _][_, ..]:这两个写法是等价的,不必多说——列表模式包含一个弃元符号和范围记号,因此序列至少包含一个元素才满足该要求,不过先写 .. 还是先写 _ 都无所谓,因为编译器都能识别。

整体来说,看你个人喜好来书写代码。它们最终是一样的代码,都是序列不空且至少有一个元素。

2、三种括号一起用

假设我们有一个类型,它使用了这样的模式匹配:

根据前面的知识,你可以猜测或者推断出,该类型的最小实现逻辑吗?换言之,你知道它允许这些模式匹配同时使用的时候,至少有多少个必需的成员存在呢?我们来想一想。

首先是 ()() 代表的是它是一个元组,但不包含任何数值。如果要找到“最优解”,只需要表示出 inst 变量是不是 ITuple 的实现类型就可以了。只要它实现自 ITuple 接口,那么对象就可以支持和兼容 () 模式,不需要定义任何新成员。哪怕它不走 ITuple 派生,只要 instobject 类型,那么这种模式匹配就是成功兼容的,就不会出现语法错误。

其次是 []。列表模式要求对象至少是集合类型,那么对象至少有一个带 int 单参数的索引器,以及一个 LengthCount 属性的其中一个即可。那么,这至少就需要对象有两个成员的实现。

最后是 {}。属性模式的唯一要求是,该类型不能是指针。因为指针类型永远都不包含任何判断属性。它只能取出其中的数值(*p)然后才可能有对应的属性。因此为了尽量包容和兼容前面的模式,那么我们假设 inst 此时是 object 类型。那么由于它不是指针,因此属性模式就可以使用(即使我们知道 object 里不包含任何可访问的属性信息)。

所以,要想满足三种括号一起使用的模式匹配的话,那么至少需要对象从语法上实现两个成员(LengthCount 其一,然后一个 int 类型的单参数的索引器),然后 instITuple 的实现类型即可:

3、过于复杂的递归模式匹配

考虑一种极端情况:

这意味着什么?这意味着我在使用解构模式的时候会产生这样的代码:

这个 (({})) 是一个嵌套模式,不过没什么特殊意义。我们拆开看看就明白了。首先 (({})) 最外层是一个 () 模式,它表示对象可以解构就行,因此它等价于 s is not null;然后里面一层是 ({})()。它代表我在使用 Deconstruct 产生解构对象了之后又一次作判断。但 Deconstruct 是我写的一种极端代码:它解构了一个寂寞——返回了它自己。所以内层的 ({})() 还是跟原来判断信息完全一样。最后,最内层有一个空大括号,它表示空属性模式匹配,它依然和 o is not null 表达式等价,因此,完整的表达式和你写一个 s is {}s is not null 没有区别。

你觉得好玩的话,我这还有一个例子。

然后模式匹配:

蛇皮怪。

我们强烈不建议你这么写代码,我之所以讲这个是为了告诉你有这么一种特殊的情况。而且,虽然没有这么多层级的嵌套,但经常会有括号嵌套括号的用法,两层还是蛮常见的:

  • ((var a, var b), var c):将对象解构为两个值,用对位模式判断。其中第一个值可继续解构,并仍然使用对位模式继续对位判断;第二个值使用的是 var 模式;

  • ([var a, var b], var c):将对象解构为两个值,用对位模式判断。其中第一个值使用列表模式判断;第二个值使用的是 var 模式;

  • ({ Property: var a }, _):将对象解构为两个值,用对位模式判断。其中第一个值使用属性模式判断;第二个值使用的是弃元模式;

  • [(var a, var b), var c]:使用列表模式判断对象是否只有两个值。其中列表模式里的第一个值可解构为两个值,并都使用 var 模式;第二个值使用的是 var 模式;

  • [[var a, ..], [.., var b]]:使用列表模式判断对象是否只有两个值。其中第一个值可继续使用列表模式判断,并只判断该列表里的其中第一个值,使用 var 模式;第二个值也使用列表模式判断,且只判断该列表里的最后一个值,使用 var 模式;

  • [{ P1: 42 }, [var p2], (p3: 0)]:使用列表模式判断对象是否只有三个值。其中第一个值使用属性模式判断 P1 属性;第二个值使用列表模式判断该列表是否只包含一个值,并使用 var 模式将该值取出;第三个值使用对位模式判断 p3

  • { Property: (var a, var b, _) }:使用属性模式判断属性 Property。该属性的结果可继续使用对位模式判断三个值,前两个值都用 var 模式,最后一个是弃元模式;

  • { Property: [var a, .., var b, _] }:使用属性判断属性 Property。该属性的结果可继续使用列表模式,并判断第一个元素和倒数第二个元素,都用 var 模式;

  • { Property: { Nested1: 42, Nested2: 0 } }:使用属性模式判断属性 Property。该属性的返回值还可继续使用属性模式判断其中的 Nested1Nested2 模式。



C# 模式匹配的综合应用的评论 (共 条)

分享到微博请遵守国家法律