Udostępnij za pośrednictwem


Dopasowanie wzorca Span<char> na ciągu stałym

Notatka

Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.

Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).

Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .

Streszczenie

Zezwalaj na dopasowywanie wzorca Span<char> i ReadOnlySpan<char> w ciągu stałym.

Motywacja

Ze względu na wydajność, w wielu scenariuszach preferowane jest używanie Span<char> i ReadOnlySpan<char> zamiast ciągów. Aby umożliwić korzystanie z ReadOnlySpan<char> zamiast string, platforma dodała wiele nowych interfejsów API.

Typową operacją na ciągach jest użycie przełącznika do testowania, czy jest to określona wartość, a kompilator optymalizuje taki przełącznik. Jednak obecnie nie ma możliwości wykonania tego samego na ReadOnlySpan<char> w sposób wydajny, poza ręczną implementacją przełącznika i optymalizacją.

Aby zachęcić do przyjęcia ReadOnlySpan<char> zezwalamy na dopasowywanie wzorca ReadOnlySpan<char>na stałym string, co pozwala również na jego użycie w przełączniku.

static bool Is123(ReadOnlySpan<char> s)
{
    return s is "123";
}

static bool IsABC(Span<char> s)
{
    return s switch { "ABC" => true, _ => false };
}

Szczegółowy projekt

Zmieniamy spec dla stałych wzorców w sposób następujący (proponowany dodatek jest wyświetlany pogrubioną czcionką):

Biorąc pod uwagę wartość wejściową wzorca e i stały wzorzec P z przekonwertowaną wartością v,

  • jeśli e ma typ całkowity lub typ wyliczeniowy albo formę dopuszczaną do wartości null jednego z nich, a v ma typ całkowity, wzorzec Ppasuje wartości e, jeśli wynik wyrażenia e == v jest true; inaczej
  • Jeśli e jest typu System.Span<char> lub System.ReadOnlySpan<char>, a c jest ciągiem stałym, a c nie ma stałej wartości null, wzorzec jest traktowany jako zgodny, jeśli System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c)) zwraca true.
  • wzorzec Ppasuje wartości e, jeśli object.Equals(e, v) zwraca wartość true.

Dobrze znani członkowie

System.Span<T> i System.ReadOnlySpan<T> są dopasowywane według nazwy, muszą być ref structs i można je zdefiniować poza corlibem.

System.MemoryExtensions pasuje do nazwy i można go zdefiniować poza corlib.

Podpis przeciążeń System.MemoryExtensions.SequenceEqual musi być zgodny z następującymi elementami:

  • public static bool SequenceEqual<T>(System.Span<T>, System.ReadOnlySpan<T>)
  • public static bool SequenceEqual<T>(System.ReadOnlySpan<T>, System.ReadOnlySpan<T>)

Podpis System.MemoryExtensions.AsSpan musi być zgodny z następującymi elementami:

  • public static System.ReadOnlySpan<char> AsSpan(string)

Metody z opcjonalnymi parametrami są wykluczone z uwagi.

Minusy

Żaden

Alternatywy

Żaden

Nierozwiązane pytania

  1. Czy dopasowanie powinno być definiowane niezależnie od MemoryExtensions.SequenceEqual() itp.?

    ... wzorzec jest uznawany za zgodny, jeśli e.Length == c.Length i e[i] == c[i] są takie same dla wszystkich znaków w e.

    Zalecenie: zdefiniuj MemoryExtensions.SequenceEqual() dla wydajności. Jeśli brakuje MemoryExtensions, zgłoś błąd kompilacji.

  2. Czy dopuszczalne jest dopasowanie do (string)null?

    Jeśli tak, czy (string)null podsumować "" od MemoryExtensions.AsSpan(null) == MemoryExtensions.AsSpan("")?

    static bool IsEmpty(ReadOnlySpan<char> span)
    {
        return span switch
        {
            (string)null => true, // ok?
            "" => true,           // error: unreachable?
            _ => false,
        };
    }
    

    Zalecenie: wzorzec stały (string)null powinien być zgłaszany jako błąd.

  3. Czy wzorzec stały powinien zawierać test typu środowiska uruchomieniowego wartości wyrażenia dla Span<char> lub 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?
    }
    

    Zalecenie: brak niejawnego testu typu środowiska uruchomieniowego dla wzorca stałego. (IsABC<T>() przykład jest dozwolony, ponieważ test typu jest jawny.

    To zalecenie nie zostało zaimplementowane. Wszystkie powyższe przykłady generują błąd kompilatora.

  4. Czy subsumcja powinna uwzględniać stałe wzorce ciągów, wzorce list i wzorzec właściwości Length?

    static int ToNum(ReadOnlySpan<char> s)
    {
        return s switch
        {
            { Length: 0 } => 0,
            "" => 1,        // error: unreachable?
            ['A',..] => 2,
            "ABC" => 3,     // error: unreachable?
            _ => 4,
        };
    }
    

    Zalecenie: To samo zachowanie subsumcji, które jest stosowane, gdy wartość wyrażenia wynosi string. (Czy to oznacza brak podsumpcji między ciągami stałymi, wzorcami listy i Length, oprócz traktowania [..] jako dopasowujący się do wszystkich?)

Spotkania projektowe

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