Dela via


Mönstermatch Span<char> på en konstant sträng

Obs

Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.

Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader fångas upp i de relevanta anteckningarna från språkdesignmöten (LDM).

Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.

Champion-fråga: https://github.com/dotnet/csharplang/issues/8640

Sammanfattning

Tillåt mönster som matchar en Span<char> och en ReadOnlySpan<char> på en konstant sträng.

Motivation

För prestanda prefereras användning av Span<char> och ReadOnlySpan<char> framför strängar i många scenarier. Ramverket har lagt till många nya API:er så att du kan använda ReadOnlySpan<char> i stället för en string.

En vanlig åtgärd på strängar är att använda en växel för att testa om det är ett visst värde, och kompilatorn optimerar en sådan växel. Det finns dock för närvarande inget sätt att göra samma sak på ett ReadOnlySpan<char> effektivt, förutom att implementera växeln och optimeringen manuellt.

För att uppmuntra till införande av ReadOnlySpan<char> tillåter vi mönster som matchar en ReadOnlySpan<char>, på en konstant string, vilket också gör att den kan användas i en växel.

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

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

Detaljerad design

Vi ändrar specifikationen för konstanta mönster enligt följande (det föreslagna tillägget visas i fetstil):

Givet ett mönsterindatavärde e och ett konstant mönster P med konverterat värde v,

  • om e har en integrerad typ eller uppräkningstyp, eller en nullbar form av en av dessa, och v har en integrerad typ, matchar mönstret P värdet e om resultatet av uttrycket e == v är true; annars
  • Om e är av typen System.Span<char> eller System.ReadOnlySpan<char>och c är en konstant sträng och c inte har ett konstant värde på nullanses mönstret matcha om System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c)) returnerar true.
  • mönstret Pmatchar värdet e om object.Equals(e, v) returnerar true.

Välkända medlemmar

System.Span<T> och System.ReadOnlySpan<T> matchas med namn, måste vara ref structs och kan definieras utanför corlib.

System.MemoryExtensions matchas med namn och kan definieras utanför corlib.

Signaturen för System.MemoryExtensions.SequenceEqual-överlagringar måste matcha:

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

Signaturen för System.MemoryExtensions.AsSpan måste matcha:

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

Metoder med valfria parametrar undantas från övervägande.

Nackdelar

Ingen

Alternativ

Ingen

Olösta frågor

  1. Ska matchning definieras oberoende av MemoryExtensions.SequenceEqual() osv.

    ... mönstret anses vara matchande om e.Length == c.Length och e[i] == c[i] stämmer för alla tecken i e.

    Rekommendation: Definiera i termer av MemoryExtensions.SequenceEqual() för prestanda. Om MemoryExtensions saknas, rapportera kompileringsfel.

  2. Bör matchning mot (string)null tillåtas?

    I så fall, bör (string)null omfatta "" eftersom MemoryExtensions.AsSpan(null) == MemoryExtensions.AsSpan("")?

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

    Rekommendation: Konstant mönster (string)null ska rapporteras som ett fel.

  3. Ska mönsterjämförelsen för konstanter innehålla ett typtest vid körning av uttrycksvärdet för Span<char> eller 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?
    }
    

    Rekommendation: Inget implicit körningstyptest för konstant mönster. (IsABC<T>() exempel tillåts eftersom typtestet är explicit.)

    Den här rekommendationen implementerades inte. Alla föregående exempel genererar ett kompilatorfel.

  4. Ska subsumtion beakta konstanta strängmönster, listmönster och Length egenskapsmönster?

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

    Rekommendation: Samma undersumtionsbeteende som används när uttrycksvärdet är string. (Innebär det ingen inkludering mellan konstanta strängar, listmönster och Length, förutom att [..] behandlas som ett som matchar vad som helst?)

Designa möten

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