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

C# 解构模式

2023-01-14 22:26 作者:SunnieShine  | 我要投稿

1、语法

因为前文我们拥有了解构函数,也拥有了 var 模式,因此 C# 灵活的语法提供了 var 模式的解构版本:

稍微注意一下这里的语法是写成 var (x, y)。当然,你也可以内联 var 关键字。和值元组的语法一致,你依然可以用 (var x, var y) 的语法。

这样是可以的。

不过严谨一点的话,var (x, y) 是解构模式,而 (var x, var y) 是对位模式。因为前者使用 var (x, y) 语法,小括号里直接定义了变量名,小括号的外侧则是 var 关键字;但 (var x, var y) 在小括号里定义了两个变量,都使用了 var 关键字,这意味着是对应位置上的数据分别定义变量,类似 point.X is var x && point.Y is var y 的效果,因此只能说是对位模式。

使用解构模式可以更清楚、更简明地将对象进行解构,直接赋值到变量上;但它存在一定的弊端,例如解构模式下就不能往里判断数值了。也就是说,你在写成 var (a, b) 的类似语法后,就无法往 ab 上使用任何模式匹配的判别语法了。该嵌套模式匹配之语法将在稍后说明。

2、可空值类型解构模式的别样意义

在 C# 里,可空值类型一直是一种方便也不方便的数据类型。它的声明和使用都比较方便,但问题就出在它可能是 null 数值。假设前文的 Point 我们用的是可空类型的话:

此时,我们在后续的代码里,无从根据代码直接确定 nullable 是否为 null(除非看取了 nullable 的值才行)。因此,一旦我们对这个类型进行解构:

这就不单纯和 var 模式一样。它牵扯到数据是不是 null 才可解构的问题。如果数据都是 null 了,我们就无法解构。因此,可空值类型的解构模式会先判断对象是不是不为 null,然后才是解构。

nullable != nullnullable.HasValue 是等效的,所以写 nullable.HasValue 也没问题。

3、主构造器的解构模式

是的,主构造器会自动生成对应的解构函数,因此完全可以直接使用解构模式。还是使用之前的 Person 类型:

那么,有这样的语法:

这样是允许的。但你不能写 is Person (name, _, isBoy),因为前面的 var 关键字是这个模式匹配的固定格式,改成了 Person 的话,后面就只能看成对位模式了。

4、调用扩展方法的解构模式

解构模式和对位模式类似,编译器也支持嗅探解构模式对应的扩展方法。一般正常的实现我们可能对一些数据类型无法实现解构操作,因此我们需要扩展方法来达到一些行为。比如假设我要去获取数组的前两个元素,我们经常会使用 [0][1] 来获取,不过现在我们可以使用解构模式来完成:

假设我随便这么写了这个扩展方法,它们用于解构 T[] 数组。于是我们可以对一个一维数组进行解构操作:

请注意解构函数正常使用的时候是尽量不出现 0 或 1 个元素的解构模式,不过在这个时候也可能会遇到,因此语法没有对此进行限制。

5、解构和对位模式不要求判断元素数量至少两个

这里稍微说一个比较不容易了解到的知识点。编译器限制我们定义一个至少两个元素的值元组 ValueTuple 类型,也就是说,一个或零个的值元组类型是不被允许的:

注意,第 2 行的代码我们假设写的 var 而不是 ValueTuple<int> 的话,编译器会自动消去 (1) 两侧的小括号,然后直接认为它是 1;故意显式给出类型名是为了告诉你,这两个情况都是值元组不被允许的。

不过,虽然解构模式和对位模式长得都跟值元组的类型声明模式很像,但对位模式和解构模式允许和支持解构函数可以包含任意多的 out 参数用于解构,这也意味着在解构模式和对位模式里,is ()is (1) 是存在的语法。

6、单元素的解构模式要手动消除二义性

在 C# 里,小括号如果不需要是会被编译器分析出来的。比如说 var a = (1 + 3),此时的小括号没有必要需要它。在模式匹配里,单元素的解构模式也是一种特殊的处理:它会被视为常量模式,于是,考虑下面的例子,判断就有些奇怪了:

请看这样的代码。你认为它是对的吗?答案是不对。编译器会首先认为 (42) 是常量模式,而 o 变量是 C 类型而不是一个整数,因此这个模式会导致编译器直接告知“永远都不会匹配成功”的编译器错误。

那么,怎么让它调用该解构函数来完成判别呢?答案其实很简单:消除编译器认为是常量模式的二义性即可。比如给 (42) 模式添加参数名。

这样编译器就不会简单认为是一个常量了。

7、任意类型的解构模式

对任何数据类型(当然,指针类型除外)而言,我们都是可以使用解构模式的。这一点很神奇。

可,这会被视为什么判断规则呢?不知道你知不知道一个类型叫 ITuple?这个数据类型限制了类型具有元组的性质。所以,对任何数据类型来说的解构模式,实际上是被编译器特殊处理和优化过,并认为是在匹配该类型的数据规则。

比如 o is () 会被视为 o is ITuple tuple && tuple.Length == 0。注意此时 ITuple 里的 Length 属性表示的是元组的元素数。当然了,如果你这个类型具有解构函数,就不会走这个路线去判断。但是,如果一个类型既没有实现这个 ITuple 接口,又没有匹配的解构函数,就会产生编译器错误。


C# 解构模式的评论 (共 条)

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