常量字符串上的模式匹配 Span<char>
注意
本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 ECMA 规范中。
功能规范与已完成的实现之间可能存在一些差异。 这些差异记录在相关的 语言设计会议(LDM)说明中。
可以在 规范一文中详细了解将功能规范采用 C# 语言标准的过程。
总结
允许在常量字符串上对 Span<char>
和 ReadOnlySpan<char>
进行模式匹配。
动机
为了性能,在许多场景中,建议优先使用 Span<char>
和 ReadOnlySpan<char>
而不是字符串。 框架添加了许多新 API,使你能够使用 ReadOnlySpan<char>
代替 string
。
字符串上的常见操作是使用开关来测试它是否为特定值,编译器优化此类开关。 但是,除了手动实现开关和优化之外,目前无法有效地在 ReadOnlySpan<char>
上执行相同的操作。
为了鼓励采用 ReadOnlySpan<char>
,我们允许在常量 string
上对 ReadOnlySpan<char>
进行模式匹配,从而也允许在 switch 中使用它。
static bool Is123(ReadOnlySpan<char> s)
{
return s is "123";
}
static bool IsABC(Span<char> s)
{
return s switch { "ABC" => true, _ => false };
}
详细设计
我们将按如下方式更改常量模式规范 (建议的新增部分以粗体显示):
给定模式输入值
e
和具有转换值v
的常量模式P
,
- 如果 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
,则模式被视为匹配。- 如果
object.Equals(e, v)
返回true
,则模式P
匹配值 e。
知名成员
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
中所有字符对于e.Length == c.Length
和e[i] == c[i]
都匹配,则模式被视为匹配。建议:根据
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
之间没有包含,而是将[..]
视为匹配任何项?)