常量字符串上的模式匹配 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)

从考虑中排除具有可选参数的方法。

缺点

没有

替代方案

没有

未解决的问题

  1. 是否应独立定义匹配,而非依赖于像 MemoryExtensions.SequenceEqual() 这样的元素?

    ... 如果 e 中所有字符对于 e.Length == c.Lengthe[i] == c[i] 都匹配,则模式被视为匹配。

    建议:根据 MemoryExtensions.SequenceEqual() 的性能进行定义。 如果缺少 MemoryExtensions,则报告编译错误。

  2. 是否应该允许与 (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 报告为错误。

  3. 常量模式匹配是否应包括对 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>() 示例,因为类型测试是显式的。

    未实施此建议。 上述所有示例都生成编译器错误。

  4. 包含是否应考虑常量字符串模式、列表模式和 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 之间没有包含,而是将 [..] 视为匹配任何项?)

设计会议

https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-07.md#readonlyspanchar-patterns