列表模式

注意

本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 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] 等效于以下代码(如果通过显式 IndexRange 支持兼容):

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.SubstringRuntimeHelpers.GetSubArray

未解决的问题

  1. 我们应该支持多维数组吗? (回答 [LDM 2021-05-26]:未被支持。如果我们想要创建一个以MD数组为重点的常规版本,我们希望重新审视其目前缺失的所有方面,而不仅仅是列表模式。
  2. 我们应该接受 slice_pattern.. 之后的一般模式吗? (回答 [LDM 2021-05-26]:是的,切片后允许使用任何模式。
  3. 根据此定义,模式 [..] 用于测试 expr.Length >= 0。 我们是否应该省略这种测试,假设 Length 总是非负的? (回答 [LDM 2021-05-26]:[..] 不会发出长度检查)