常量字符串上的模式匹配 Span<char>
注意
本文是特性规范。 此规范是功能的设计文档。 它包括建议的规范变更,以及功能设计和开发过程中所需的信息。 这些文章将持续发布,直至建议的规范变更最终确定并纳入当前的 ECMA 规范。
功能规范与已完成的实现之间可能存在一些差异。 这些差异记录在相关的语言设计会议 (LDM) 说明中。
可以在有关规范的文章中了解更多有关将功能规范子块纳入 C# 语言标准的过程。
支持者问题:https://github.com/dotnet/csharplang/issues/8640
总结
允许在常量字符串上对 Span<char>
和 ReadOnlySpan<char>
进行模式匹配。
动机
为了性能,在许多情况下,Span<char>
和 ReadOnlySpan<char>
的使用优于字符串。 框架添加了许多新 API,使你能够使用 ReadOnlySpan<char>
代替 string
。
对字符串的一个常见操作是使用开关来测试它是否是特定值,编译器会优化这样的开关。 然而,目前除了手动实现开关和优化外,没有其他方法可以有效地在 ReadOnlySpan<char>
上执行相同的操作。
为了鼓励采用 ReadOnlySpan<char>
,我们允许在常量 ReadOnlySpan<char>
上对 string
进行模式匹配,从而也允许在 switch 中使用它。
static bool Is123(ReadOnlySpan<char> s)
{
return s is "123";
}
static bool IsABC(Span<char> s)
{
return s switch { "ABC" => true, _ => false };
}
详细设计
我们对规范的常量模式修改如下(建议添加的部分以粗体显示):
给定一个模式输入值
e
和一个常量模式P
,其转换值为v
,
- 如果 e 具有整数类型或枚举类型,或其中一种可为 null 形式,并且 v 具有整数类型,则如果表达式
e == v
的结果为true
,则模式P
匹配值 e;否则- 如果 e 的类型为
System.Span<char>
或System.ReadOnlySpan<char>
,并且 c 是常量字符串,并且 c 没有常量值null
,则如果System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c))
返回true
,则模式被视为匹配。- 当
P
返回 时,模式object.Equals(e, v)
与值true
匹配。
知名成员
System.Span<T>
和 System.ReadOnlySpan<T>
按名称匹配,必须是 ref struct
,并且可以在 corlib 外部定义。
System.MemoryExtensions
按名称匹配,并且可以在 corlib 外部定义。
System.MemoryExtensions.SequenceEqual
重载的签名必须匹配:
public static bool SequenceEqual<T>(System.Span<T>, System.ReadOnlySpan<T>)
public static bool SequenceEqual<T>(System.ReadOnlySpan<T>, System.ReadOnlySpan<T>)
System.MemoryExtensions.AsSpan
的签名必须匹配:
public static System.ReadOnlySpan<char> AsSpan(string)
具有可选参数的方法不在考虑范围内。
缺点
无
替代方案
无
未解决的问题
是否应独立定义匹配,而非依赖于像
MemoryExtensions.SequenceEqual()
这样的元素?... 如果
e.Length == c.Length
中所有字符对于e[i] == c[i]
和e
都匹配,则模式被视为匹配。建议:为提高性能,按照
MemoryExtensions.SequenceEqual()
的方式进行定义。 如果缺少MemoryExtensions
,则报告编译错误。是否应该允许与
(string)null
匹配?如果是这样,那么
(string)null
是否应该包含""
,因为MemoryExtensions.AsSpan(null) == MemoryExtensions.AsSpan("")
?static bool IsEmpty(ReadOnlySpan<char> span) { return span switch { (string)null => true, // ok? "" => true, // error: unreachable? _ => false, }; }
建议:常量模式
(string)null
应报告为错误。常量模式匹配是否应包括对
Span<char>
或ReadOnlySpan<char>
的表达式值的运行时类型测试?static bool Is123<T>(Span<T> s) { return s is "123"; // test for Span<char>? } static bool IsABC<T>(Span<T> s) { return s is Span<char> and "ABC"; // ok? } static bool IsEmptyString<T>(T t) where T : ref struct { return t is ""; // test for ReadOnlySpan<char>, Span<char>, string? }
建议:没有常量模式的隐式运行时类型测试。 (允许
IsABC<T>()
示例,因为类型测试是显式的。未实施此建议。 前面的所有示例都会产生编译器错误。
包含是否应考虑常量字符串模式、列表模式和
Length
属性模式?static int ToNum(ReadOnlySpan<char> s) { return s switch { { Length: 0 } => 0, "" => 1, // error: unreachable? ['A',..] => 2, "ABC" => 3, // error: unreachable? _ => 4, }; }
建议:与表达式值为
string
时使用的包含行为相同。 (这是否意味着常量字符串、列表模式和Length
之间没有包含,而是将[..]
视为匹配任何项?)