列表模式
注意
本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 ECMA 规范中。
功能规范与已完成的实现之间可能存在一些差异。 这些差异记录在相关的 语言设计会议(LDM)记录中。
可以在 规范一文中详细了解将功能规范采用 C# 语言标准的过程。
支持者问题:https://github.com/dotnet/csharplang/issues/3435
总结
允许将数组或列表与一系列模式(例如,array is [1, 2, 3]
)匹配长度为 3 的整数数组,其元素分别为 1、2、3。
详细设计
模式语法的修改方式如下:
list_pattern_clause
: '[' (pattern (',' pattern)* ','?)? ']'
;
list_pattern
: list_pattern_clause simple_designation?
;
slice_pattern
: '..' pattern?
;
primary_pattern
: list_pattern
| slice_pattern
| // all of the pattern forms previously defined
;
有两个新模式:
- list_pattern 用于匹配元素。
- slice_pattern 仅允许一次,并且只能直接在 list_pattern_clause 中使用,并丢弃零个或多个元素。
模式兼容性
list_pattern 与任何可计数及可索引的类型兼容 —— 其具有一个可访问的索引器,以 Index
作为参数,或者有一个带有单个 int
参数的可访问索引器。 如果两个索引器都存在,则首选前者。
具有子模式的 slice_pattern 与任何可计数以及可切片的类型兼容 —— 其具有一个可访问的索引器,以 Range
作为参数,或者是一个具有两个 int
参数的可访问的 Slice
方法。 如果两者都存在,则首选前者。
在没有子模式的情况下,slice_pattern 可以兼容任何与 list_pattern 兼容的类型。
此规则集派生自 范围索引器模式。
包含性检查
包含性检查的工作原理与 位置模式搭配 ITuple
的情况类似,相应的子模式按位置匹配,外加一个用于测试长度的节点。
例如,以下代码生成一个错误,因为两种模式都会生成相同的 DAG:
case [_, .., 1]: // expr.Length is >= 2 && expr[^1] is 1
case [.., _, 1]: // expr.Length is >= 2 && expr[^1] is 1
不同于:
case [_, 1, ..]: // expr.Length is >= 2 && expr[1] is 1
case [.., 1, _]: // expr.Length is >= 2 && expr[^2] is 1
未指定运行时匹配子模式的顺序,并且失败的匹配可能不会尝试匹配所有子模式。
给定特定长度后,两个子模式可能引用同一元素,在这种情况下,此值的测试将插入到决策 DAG 中。
- 例如,
[_, >0, ..] or [.., <=0, _]
变为length >= 2 && ([1] > 0 || length == 3 || [^2] <= 0)
,其中 3 的长度值表示另一个测试。 - 相反,
[_, >0, ..] and [.., <=0, _]
变为length >= 2 && [1] > 0 && length != 3 && [^2] <= 0
,其中 3 的长度值不允许另一个测试。
因此,对于类似 case [.., p]: case [p]:
这样的情况会出现错误,因为在运行时,我们在第二种情况下匹配了相同的元素。
如果切片子模式与列表或长度值匹配,则子模式被视为包含列表的直接子模式。 例如,[..[1, 2, 3]]
包含某种形式的 [1, 2, 3]
模式。
对当前使用的成员作出以下假设:
- 当且仅当类型可索引时,假定使类型可计数的属性总是返回非负值。 例如,模式
{ Length: -1 }
永远无法匹配数组。 - 假设使类型可切片的成员行为良好,也就是说,返回值永远不会为 null,并且它是包含列表的适当子切片。
如果上述任何假设不保留,则模式匹配操作的行为是未定义的。
降低
表单 expr is [1, 2, 3]
模式等效于以下代码:
expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3
slice_pattern 的行为类似于适当的放弃,即不会针对此类模式发出任何测试,而只会影响其他节点,即长度和索引器。 例如,模式 expr is [1, .. var s, 3]
等效于以下代码(如果通过显式 Index
和 Range
支持兼容):
expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3
slice_pattern 的输入类型是基础 this[Range]
或 Slice
方法的返回类型,但有两个例外:对于 string
和数组,将分别使用 string.Substring
和 RuntimeHelpers.GetSubArray
。
未解决的问题
- 我们应该支持多维数组吗? (回答 [LDM 2021-05-26]:未被支持。如果我们想要创建一个以MD数组为重点的常规版本,我们希望重新审视其目前缺失的所有方面,而不仅仅是列表模式。
- 我们应该接受 slice_pattern 中
..
之后的一般模式吗? (回答 [LDM 2021-05-26]:是的,切片后允许使用任何模式。 - 根据此定义,模式
[..]
用于测试expr.Length >= 0
。 我们是否应该省略这种测试,假设Length
总是非负的? (回答 [LDM 2021-05-26]:[..]
不会发出长度检查)