Compartir vía


Coincidencia de patrones de Span<char> en una cadena de constante

Nota

Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos e se incorporan en la especificación ECMA actual.

Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.

Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones de .

Resumen

Permitir la coincidencia de un patrón Span<char> y un ReadOnlySpan<char> en una cadena constante.

Motivación

Para el rendimiento, se prefiere el uso de Span<char> y ReadOnlySpan<char> sobre la cadena de caracteres en muchos escenarios. El marco ha agregado muchas API nuevas para permitirle usar ReadOnlySpan<char> en lugar de un string.

Una operación común en cadenas es usar un modificador para probar si es un valor determinado y el compilador optimiza dicho modificador. Sin embargo, actualmente no hay ninguna manera de hacer lo mismo en una ReadOnlySpan<char> eficazmente, aparte de implementar el conmutador y la optimización manualmente.

Para fomentar la adopción de ReadOnlySpan<char> permitimos que el patrón coincida con un ReadOnlySpan<char>, en una constante string, lo que también permite su uso en un conmutador.

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

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

Diseño detallado

Modificamos la especificación de para patrones constantes como se indica a continuación (la adición propuesta se muestra en negrita):

Dado un valor de patrón de entrada e y un patrón constante P, cuyo valor convertido es v,

  • si e tiene tipo entero o tipo de enumeración, o una forma que acepta valores NULL de uno de ellos y v tiene un tipo entero, el patrón Pcoincide con el valor e si el resultado de la expresión e == v es true; de otra manera
  • Si e es de tipo System.Span<char> o System.ReadOnlySpan<char>, y c es una cadena constante y c no tiene un valor constante de null, el patrón se considera coincidente si System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c)) devuelve true.
  • el patrón Pcoincide con el valor e si object.Equals(e, v) devuelve true.

Miembros conocidos

System.Span<T> y System.ReadOnlySpan<T> coinciden por nombre, deben ser ref structs y se pueden definir fuera de corlib.

System.MemoryExtensions coincide con el nombre y se puede definir fuera de corlib.

La firma de las sobrecargas de System.MemoryExtensions.SequenceEqual debe coincidir con:

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

La firma de System.MemoryExtensions.AsSpan debe coincidir con:

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

Los métodos con parámetros opcionales se excluyen de la consideración.

Inconvenientes

Ninguno

Alternativas

Ninguno

Preguntas sin resolver

  1. ¿Debe definirse la coincidencia independientemente de MemoryExtensions.SequenceEqual() etc.?

    ... el patrón se considera coincidente si e.Length == c.Length y e[i] == c[i] para todos los caracteres de e.

    Recomendación: defina en términos de MemoryExtensions.SequenceEqual() para el rendimiento. Si falta MemoryExtensions, notifique el error de compilación.

  2. ¿Se debe permitir la coincidencia con (string)null?

    Si es así, ¿debe (string)null subsumir a "" ya que MemoryExtensions.AsSpan(null) == MemoryExtensions.AsSpan("")?

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

    Recomendación: el patrón constante (string)null debe notificarse como un error.

  3. ¿Debe la coincidencia de patrones constantes incluir una prueba de tipo en tiempo de ejecución del valor de expresión para Span<char> o 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?
    }
    

    Recomendación: no hay pruebas implícitas de tipo en tiempo de ejecución para el patrón constante. (El ejemploIsABC<T>() se permite porque la prueba de tipo es explícita).

    Esta recomendación no se implementó. Todos los ejemplos anteriores producen un error del compilador.

  4. ¿Debe la subsumpción considerar los patrones de cadena constantes, los patrones de lista y el patrón de propiedad Length?

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

    Recomendación: usar el mismo comportamiento de subsunción que se emplea cuando el valor de la expresión es string. (¿Esto significa que no hay subsumición entre cadenas constantes, patrones de lista y Length, aparte de tratar [..] como que coincide con cualquiera?)

Reuniones de diseño

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