Поделиться через


Сопоставление шаблонов Span<char> в постоянной строке

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время дизайна и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия зафиксированы в соответствующих заметках собраний по проектированию языка (LDM).

Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .

Сводка

Разрешить совпадение шаблона для Span<char> и ReadOnlySpan<char> в константной строке.

Мотивация

Для производительности использование Span<char> и ReadOnlySpan<char> предпочтительнее по сравнению со строкой во многих сценариях. Платформа добавила множество новых API, которые позволяют использовать ReadOnlySpan<char> вместо string.

Распространенная операция со строками заключается в использовании переключателя для проверки того, является ли это определенное значение, и компилятор оптимизирует такой коммутатор. Однако в настоящее время нет возможности сделать то же самое на ReadOnlySpan<char> эффективно, кроме как путем ручной реализации переключения и оптимизации.

Чтобы поощрять внедрение ReadOnlySpan<char>, мы разрешаем сопоставление шаблонов ReadOnlySpan<char>на константном string, что также позволяет использовать его в коммутаторе.

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 имеет целочисленный тип или тип перечисления, или пустую форму одного из них, а v имеет целочисленный тип, шаблон Pсоответствует значению e, если результат выражения e == v равен true; иначе
  • Если e имеет тип System.Span<char> или System.ReadOnlySpan<char>, а c является константной строкой, а c не имеет константного значения null, то шаблон считается совпадающим, если System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c)) возвращает true.
  • Шаблон Pсоответствует значению e, если object.Equals(e, v) возвращает true.

Известные члены

System.Span<T> и System.ReadOnlySpan<T> совпадают по имени, должны быть ref structи могут быть определены вне corelib.

System.MemoryExtensions совпадает по имени и может быть определён вне corelib.

Сигнатура перегрузки 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.Length == c.Length и e[i] == c[i] для всех символов в e.

    Рекомендация: Определите в терминах 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