Compartir a través de


Literales de cadena Utf8

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 y 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 especificaciones.

Problema planteado por experto: https://github.com/dotnet/csharplang/issues/184

Resumen

Esta propuesta incluye la capacidad de escribir literales de cadena UTF8 en C# y hacer que se codifiquen automáticamente en su representación de byte UTF-8.

Motivación

UTF8 es el formato de codificación de la web y su uso es necesario en partes significativas del entorno .NET. Aunque gran parte de los datos vienen en forma de byte[] fuera de la pila de red todavía hay usos significativos de constantes en el código. Por ejemplo la pila de red tiene que escribir comúnmente constantes como "HTTP/1.0\r\n", " AUTH" o . "Content-Length: ".

En la actualidad, no hay ninguna sintaxis eficaz para hacerlo, ya que C# representa todas las cadenas que usan codificación UTF16. Esto significa que los desarrolladores tienen que elegir entre la comodidad de codificar en tiempo de ejecución, lo que conlleva una sobrecarga, incluido el tiempo empleado en el inicio al realizar efectivamente la operación de codificación (y las asignaciones si tienen como objetivo un tipo que realmente no las requiere), o traducir manualmente los bytes y almacenarlos en un byte[].

// Efficient but verbose and error prone
static ReadOnlySpan<byte> AuthWithTrailingSpace => new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
WriteBytes(AuthWithTrailingSpace);

// Incurs allocation and startup costs performing an encoding that could have been done at compile-time
static readonly byte[] s_authWithTrailingSpace = Encoding.UTF8.GetBytes("AUTH ");
WriteBytes(s_authWithTrailingSpace);

// Simplest / most convenient but terribly inefficient
WriteBytes(Encoding.UTF8.GetBytes("AUTH "));

Esta compensación es un punto problemático que surge con frecuencia para nuestros socios en el tiempo de ejecución, ASP.NET y Azure. A menudo les hace dejar el rendimiento sobre la mesa porque no quieren pasar por la molestia de escribir el encoding byte[] a mano.

Para corregir esto, permitiremos literales UTF8 en el lenguaje y los codificaremos en UTF8 byte[] en tiempo de compilación.

Diseño detallado

u8 sufijo en literales de cadena

El lenguaje proporcionará el sufijo u8 en los literales de cadena para forzar que el tipo sea UTF8. El sufijo no distingue mayúsculas de minúsculas, se admitirá el sufijo U8 y tendrá el mismo significado que el sufijo u8.

Cuando se usa el sufijo u8, el valor del literal es un ReadOnlySpan<byte> que contiene una representación de bytes UTF-8 de la cadena. Un terminador NULL se coloca por encima del último byte en la memoria (y fuera de la longitud del ReadOnlySpan<byte>) para controlar algunos casos de interoperabilidad en los que la llamada espera cadenas terminadas en NULL.

string s1 = "hello"u8;             // Error
var s2 = "hello"u8;                // Okay and type is ReadOnlySpan<byte>
ReadOnlySpan<byte> s3 = "hello"u8; // Okay.
byte[] s4 = "hello"u8;             // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'byte[]'.
byte[] s5 = "hello"u8.ToArray();   // Okay.
Span<byte> s6 = "hello"u8;         // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'System.Span<byte>'.

Dado que los literales se asignarían como constantes globales, el tiempo de vida ReadOnlySpan<byte> de la resultante no impediría que fuera devuelta o pasada a otro lugar. Sin embargo, ciertos contextos, más notablemente dentro de funciones async, no permiten locales de tipos ref struct, por lo que habría una penalización de uso en esas situaciones, siendo necesaria una llamada o similar ToArray().

Un literal u8 no tiene un valor constante. Esto se debe a que ReadOnlySpan<byte> actualmente no puede ser el tipo de una constante. Si la definición de const se expande en el futuro para tener en cuenta ReadOnlySpan<byte>, este valor también se deberá trata como una constante. Prácticamente, esto significa que no se puede usar un literal u8 como valor predeterminado de un parámetro opcional.

// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing"u8) { ... } 

Cuando el texto de entrada del literal es una cadena UTF16 con formato incorrecto, el lenguaje generará un error:

var bytes = "hello \uD8\uD8"u8; // Error: malformed UTF16 input string

var bytes2 = "hello \uD801\uD802"u8; // Allowed: invalid UTF16 values, but it's correctly formed.

Operador de suma

Se añadirá un nuevo punto a §12.10.5 Operador de adición como sigue.

  • Concatenación de representación de bytes UTF8:

    ReadOnlySpan<byte> operator +(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y);
    

    Este operador binario + realiza la concatenación de secuencias de bytes y es aplicable siempre que ambos operandos sean representaciones de bytes UTF8 en cuanto a la semántica. Un operando es semánticamente una representación de bytes UTF8 cuando es un valor de un literal u8 o un valor producido por el operador de concatenación de representación de bytes UTF8.

    El resultado de la concatenación de representaciones de bytes UTF8 es un ReadOnlySpan<byte> que consta de los bytes del operando izquierdo seguidos de los bytes del operando derecho. Un terminador NULL se coloca por encima del último byte en la memoria (y fuera de la longitud del ReadOnlySpan<byte>) para controlar algunos casos de interoperabilidad en los que la llamada espera cadenas terminadas en NULL.

Reducción

El lenguaje bajará las cadenas con codificación UTF8 exactamente como si el desarrollador hubiera escrito el literal byte[] resultante en el código. Por ejemplo:

ReadOnlySpan<byte> span = "hello"u8;

// Equivalent to

ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
                               Slice(0,5); // The `Slice` call will be optimized away by the compiler.

Esto significa que todas las optimizaciones que se aplican al formulario new byte[] { ... } también se aplicarán a literales utf8. Esto significa que el punto de llamada no requerirá asignación, ya que C# lo optimiza para almacenarlo en la sección .data del archivo PE.

Múltiples aplicaciones consecutivas de operadores de concatenación de representación de bytes UTF8 se colapsan en una única creación de ReadOnlySpan<byte> con matriz de bytes que contiene la secuencia final de bytes.

ReadOnlySpan<byte> span = "h"u8 + "el"u8 + "lo"u8;

// Equivalent to

ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
                               Slice(0,5); // The `Slice` call will be optimized away by the compiler.

Inconvenientes

Confiar en API principales

La implementación del compilador usará UTF8Encoding tanto para la detección de cadenas no válidas como para la traducción a byte[]. Las API exactas podrán depender de la plataforma de destino que use el compilador. Pero UTF8Encoding será el caballo de batalla de la implementación.

Tradicionalmente, el compilador ha evitado el uso de API en tiempo de ejecución para el procesamiento de literales. Esto se debe a que toma el control de la forma en que se procesan las constantes fuera del lenguaje y en tiempo de ejecución. Concretamente, significa que los elementos como las correcciones de errores pueden cambiar la codificación constante y que el resultado de la compilación de C# depende del tiempo de ejecución en el que se ejecuta el compilador.

Este no es un problema hipotético. Las versiones anteriores de Roslyn usaban double.Parse para controlar el análisis de constantes de coma flotante. Esto ha causado una serie de problemas. En primer lugar, implicaba que algunos valores de coma flotante tenían representaciones diferentes entre el compilador nativo y Roslyn. En segundo lugar, como .NET Core evolucionó y corrigió errores ya antiguos en el código double.Parse, esto dio lugar a que se resignificaran esas constantes en el lenguaje en función del tiempo de ejecución en el que se ejecutaba el compilador. Como consecuencia, el compilador terminaba escribiendo su propia versión del código de análisis de coma flotante y quitando la dependencia de double.Parse.

Este escenario fue discutido con el equipo de tiempo de ejecución y no creemos que tenga los mismos problemas que hemos encontrado antes. El análisis de UTF8 es estable en los entornos de ejecución y no hay problemas conocidos en esta área que puedan ser motivo de preocupaciones de compatibilidad en el futuro. Si aparece, podemos volver a evaluar la estrategia.

Alternativas

Solo tipo de destino

El diseño podría basarse únicamente en la tipificación de destino y eliminar el sufijo u8 en los literales string. Hoy en día, en la mayoría de los casos, el literal string se asigna directamente a un objeto ReadOnlySpan<byte>, por lo que es innecesario.

ReadOnlySpan<byte> span = "Hello World;" 

El sufijo u8 existe principalmente para dar cabida a dos escenarios: var y resolución de sobrecargas. Para esto último, fíjese en el siguiente caso práctico:

void Write(ReadOnlySpan<byte> span) { ... } 
void Write(string s) {
    var bytes = Encoding.UTF8.GetBytes(s);
    Write(bytes.AsSpan());
}

Dada la implementación, es mejor llamar a Write(ReadOnlySpan<byte>) y el sufijo u8 lo hace conveniente: Write("hello"u8). Falta que los desarrolladores necesiten recurrir a una conversión complicada Write((ReadOnlySpan<byte>)"hello").

Aún así, esto es un elemento de comodidad, la característica puede existir sin él y puede añadirse sin problemas más adelante.

Esperar al tipo Utf8String

Mientras que el ecosistema .NET se está estandarizando ReadOnlySpan<byte> como el tipo de cadena Utf8 de facto hoy en día, es posible que el tiempo de ejecución introduzca un tipo real Utf8String en el futuro.

Debemos evaluar nuestro diseño aquí en caso del posible cambio y reflexionar sobre si vamos a arrepentirnos de las decisiones que tomemos. Esto debe sopesarse frente a la probabilidad realista de que introduzcamos Utf8String, una probabilidad que parece disminuir cada día que encontramos ReadOnlySpan<byte> como una alternativa aceptable.

Parece poco probable que lamentar la conversión de tipo de destino entre los literales de cadena y ReadOnlySpan<byte>. El uso de ReadOnlySpan<byte> como utf8 está incrustado en nuestras APIs ahora y, por lo tanto, todavía hay valor en la conversión incluso si Utf8String aparece y es un tipo "mejor". El idioma podría simplemente preferir conversiones a Utf8String sobre ReadOnlySpan<byte>.

Parece más probable que nos retractemos al ver que el sufijo u8 apunte a ReadOnlySpan<byte> en lugar de a Utf8String. Sería similar a que lamentamos que stackalloc int[] tenga un tipo natural de int* en lugar de Span<int>. Sin embargo, esto no es un obstáculo decisivo, sólo es un inconveniente.

Conversiones entre constantes de string y secuencias de byte

No se han implementado las conversiones de esta sección. Estas conversiones siguen siendo propuestas activas.

El idioma permitirá conversiones entre constantes y secuencias de string y byte donde el texto se convierte en su representación equivalente en bytes UTF8. En concreto, el compilador permitirá string_constant_to_UTF8_byte_representation_conversion - conversiones implícitas de constantes string a byte[], Span<byte>, y ReadOnlySpan<byte>. Se añadirá un nuevo punto a la sección de conversiones implícitas §10.2. Esta conversión no es una conversión estándar: §10.4.

byte[] array = "hello";             // new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f }
Span<byte> span = "dog";            // new byte[] { 0x64, 0x6f, 0x67 }
ReadOnlySpan<byte> span = "cat";    // new byte[] { 0x63, 0x61, 0x74 }

Cuando el texto de entrada de la conversión es una cadena UTF16 con formato incorrecto, el lenguaje generará un error:

const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16

Se espera que el uso predominante de esta función sea con literales, pero funcionará con cualquier valor constante string. También se admitirá la conversión de una constante string con valor null. El resultado de la conversión será el valor default del tipo de destino.

const string data = "dog"
ReadOnlySpan<byte> span = data;     // new byte[] { 0x64, 0x6f, 0x67 }

En el caso de cualquier operación constante sobre cadenas, como +, el encoding a UTF8 se producirá en el final string frente a pasar por las partes individuales y luego concatenar los resultados. Este orden es importante porque puede afectar a que la conversión se realice correctamente o no.

const string first = "\uD83D";  // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;

Las dos partes aquí no son válidas por sí mismas, ya que son partes incompletas de un par suplente. Concretamente, no hay ninguna traducción correcta a UTF8, pero juntas forman un par suplente completo que se puede traducir correctamente a UTF8.

La string_constant_to_UTF8_byte_representation_conversion no está permitida en los árboles de expresiones Linq.

Aunque las entradas de estas conversiones son constantes y los datos están totalmente codificados en tiempo de compilación, el lenguaje no considera constante la conversión. Esto se debe a que actualmente las matrices no son constantes. Si la definición de const se amplía en el futuro para poner el foco en las matrices, también se deberán tener en cuenta estas conversiones. Prácticamente, esto significa que no se puede usar el resultado de estas conversiones como valor predeterminado de un parámetro opcional.

// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... } 

Una vez implementados los literales de cadena tendrán el mismo problema que otros literales en el lenguaje: el tipo que representen dependerá de cómo se usen. C# proporciona un sufijo literal para desambiguar el significado para otros literales. Por ejemplo, los desarrolladores pueden escribir 3.14f para forzar que el valor sea un float o 1l para forzar que el valor sea un long.

Preguntas sin resolver

Las tres primeras preguntas de diseño se relacionan con las conversiones de cadena a Span<byte>, / yReadOnlySpan<byte>. No se han implementado.

(Resuelto) Conversión entre una constante string con el valor de null y secuencias byte

Si se admite esta conversión y, si es así, no se señala cómo se realiza.

Propuesta:

Permitir conversiones implícitas de una constante de string con el valor null a byte[], Span<byte>y ReadOnlySpan<byte>. El valor del tipo de destino como resultado de la conversión es default.

Solución:

Se aprueba la propuesta: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.

(Resuelto) ¿A dónde pertenece string_constant_to_UTF8_byte_representation_conversion?

¿Es string_constant_to_UTF8_byte_representation_conversion un punto en la sección de conversiones implícitas §10.2 por sí mismo, o es parte de §10.2.11, o pertenece a algún otro grupo de conversiones implícitas existente?

Propuesta:

Es un nuevo punto en la sección §10.2 de conversiones implícitas, similar a "Conversiones implícitas de cadenas interpoladas" o "Conversiones de grupos de métodos". No parece que pertenezca a "conversiones de expresiones constantes implícitas" porque, aunque el origen es una expresión constante, el resultado nunca es una expresión constante. Además, las conversiones de expresiones constantes implícitas se consideran "conversiones implícitas estándar" (§10.4.2), lo que probablemente dé lugar a cambios significativos en el funcionamiento que impliquen conversiones definidas por el usuario.

Solución:

Vamos a presentar un nuevo tipo de conversión de la constante de cadena en bytes UTF-8: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds

(Resuelto) ¿Es string_constant_to_UTF8_byte_representation_conversion una conversión estándar?

Además de las conversiones estándar "puras" (las conversiones estándar son aquellas conversiones predefinidas que pueden producirse como parte de una conversión definida por el usuario), el compilador también trata algunas conversiones predefinidas como 'algo' estándar. Por ejemplo, una conversión implícita de cadena interpolada puede producirse como parte de una conversión definida por el usuario si hay una conversión explícita al tipo de destino en el código. Como si fuera una conversión explícita estándar, aunque es una conversión implícita no incluida explícitamente en el conjunto de conversiones implícitas o explícitas estándar. Por ejemplo:

class C
{
    static void Main()
    {
        C1 x = $"hello"; // error CS0266: Cannot implicitly convert type 'string' to 'C1'. An explicit conversion exists (are you missing a cast?)
        var y = (C1)$"dog"; // works
    }
}

class C1
{
    public static implicit operator C1(System.FormattableString x) => new C1();
}

Propuesta:

La nueva conversión no es una conversión estándar. Esto evitará cambios significativos en el funcionamiento que impliquen conversiones definidas por el usuario. Por ejemplo, no tendremos que preocuparnos de las conversiones definidas por el usuario en las conversiones literales implícitas de tuplas, etc.

Solución:

No es una conversión estándar por ahora: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.

(Resuelto) Conversión de árbol de expresión LINQ

¿Debería permitirse la conversión string_constant_to_UTF8_byte_representation_conversion en el contexto de una conversión de árbol de expresión Linq? Podemos impedirlo por ahora, o simplemente podemos incluir la forma "rebajada" en el árbol. Por ejemplo:

Expression<Func<byte[]>> x = () => "hello";           // () => new [] {104, 101, 108, 108, 111}
Expression<FuncSpanOfByte> y = () => "dog";           // () => new Span`1(new [] {100, 111, 103}) 
Expression<FuncReadOnlySpanOfByte> z = () => "cat";   // () => new ReadOnlySpan`1(new [] {99, 97, 116})

¿Qué ocurre con los literales de cadena con sufijo u8? Podríamos emerger como creaciones de matrices de bytes:

Expression<Func<byte[]>> x = () => "hello"u8;           // () => new [] {104, 101, 108, 108, 111}

Solución:

No permitido en árboles de expresión Linq: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.

(Resuelto) Tipo natural de un literal de cadena con sufijo u8

En la sección "Diseño detallado" se comenta lo siguiente: "El tipo natural será ReadOnlySpan<byte>". Y al mismo tiempo, se dice: "Cuando se usa el sufijo u8, el literal todavía se puede convertir en cualquiera de los tipos permitidos: byte[], Span<byte> o ReadOnlySpan<byte>".

Se dan ciertas desventajas en este aspecto:

  • ReadOnlySpan<byte> no está disponible en el entorno de escritorio.
  • No hay conversiones existentes de ReadOnlySpan<byte> a byte[] o Span<byte>. Para poder soportarlos probablemente necesitaremos tratar los literales como de tipo objetivo. Tanto las reglas del lenguaje como la implementación serán más complejas.

Propuesta:

El tipo natural será byte[]. Está totalmente disponible en todos los marcos de trabajo. Además, en tiempo de ejecución siempre comenzaremos creando una matriz de bytes, incluso con la propuesta original. Tampoco se necesitan reglas especiales de conversión para admitir conversiones en Span<byte> y ReadOnlySpan<byte>. Ya hay conversiones implícitas definidas por el usuario de byte[] a Span<byte> y ReadOnlySpan<byte>. Hay incluso una conversión implícita definida por el usuario que pasa a ReadOnlyMemory<byte> (consulte la pregunta "Profundidad de la conversión"). Sí que hay una desventaja y es que el lenguaje no permite encadenar conversiones definidas por el usuario. Po ello, el código siguiente no se compilará:

using System;
class C
{
    static void Main()
    {
        var y = (C2)"dog"u8; // error CS0030: Cannot convert type 'byte[]' to 'C2'
        var z = (C3)"cat"u8; // error CS0030: Cannot convert type 'byte[]' to 'C3'
    }
}

class C2
{
    public static implicit operator C2(Span<byte> x) => new C2();
}

class C3
{
    public static explicit operator C3(ReadOnlySpan<byte> x) => new C3();
}

Sin embargo, al igual que con cualquier conversión definida por el usuario, se puede utilizar una conversión explícita para hacer que una conversión definida por el usuario forme parte de otra conversión definida por el

Parece que todos los casos interesantes se van a resolver con byte[] como tipo natural, pero las reglas del lenguaje y la implementación serán significativamente más sencillas.

Solución:

Se aprueba la propuesta: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals. Probablemente querremos tener un debate más profundo sobre si los literales de cadena u8 deberían tener un tipo de matriz mutable, pero no creemos que ese debate sea necesario por ahora.

Solo se ha implementado el operador de conversión explícito.

(Resuelto) Profundidad de la conversión

¿También funcionará donde quiera que un byte[] pueda funcionar? Ten en cuenta:

static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";

Es probable que el primer ejemplo funcione debido al tipo natural que procede de u8.

El segundo ejemplo es difícil de hacer que funcione porque necesita conversiones en ambas direcciones. A menos que añadamos ReadOnlyMemory<byte> como uno de los tipos de conversión permitidos.

Propuesta:

No haga nada especial.

Solución:

No se han agregado nuevos destinos de conversión por ahora https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Ninguna de las dos conversiones compila.

(Resuelto) Interrupciones en la resolución de sobrecargas

La siguiente API resultaría ambigua:

M("");
static void M1(ReadOnlySpan<char> charArray) => ...;
static void M1(byte[] byteArray) => ...;

¿Qué debemos hacer para darle solución?

Propuesta:

Similar a https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, se actualiza el miembro de función Better (§11.6.4.3) para preferir miembros en los que ninguna de las conversiones implicadas requiera convertir constantes string a secuencias UTF8 byte.

Mejor miembro de función

... Dada una lista de argumentos A con un conjunto de expresiones de argumentos {E1, E2, ..., En} y dos miembros de función aplicables Mp y Mq con tipos de parámetros {P1, P2, ..., Pn} y {Q1, Q2, ..., Qn}, Mp se define que es un miembro de función mejor que Mq si

  1. para cada argumento, la conversión implícita de Ex a Px no es un string_constant_to_UTF8_byte_representation_conversion y, para al menos un argumento, la conversión implícita de Ex a Qx es una string_constant_to_UTF8_byte_representation_conversiono
  2. para cada argumento, la conversión implícita de Ex a Px no es una función de conversión de tipo y
    • Mp es un método no genérico o Mp es un método genérico con parámetros de tipo {X1, X2, ..., Xp} y en cada parámetro de tipo Xi el argumento de tipo se deduce de una expresión o de un tipo distinto de un function_type, y
    • en al menos un argumento, la conversión implícita de Ex a Qx es un function_type_conversion, o bien Mq es un método genérico con parámetros de tipo {Y1, Y2, ..., Yq} y en al menos un parámetro de tipo Yi el argumento de tipo se deduce de un function_type, o
  3. en cada argumento, la conversión implícita de Ex a Qx no es mejor que la conversión implícita de Ex a Px y, en al menos un argumento, la conversión de Ex a Px es mejor que la conversión de Ex a Qx.

Tenga en cuenta que la incorporación de esta regla no cubrirá escenarios en los que los métodos de instancia se vuelven aplicables y "ocultan" métodos de extensión. Por ejemplo:

using System;

class Program
{
    static void Main()
    {
        var p = new Program();
        Console.WriteLine(p.M(""));
    }

    public string M(byte[] b) => "byte[]";
}

static class E
{
    public static string M(this object o, string s) => "string";
}

El comportamiento de este código cambiará silenciosamente de imprimir "string" a imprimir "byte[]".

¿Aceptamos este cambio en el resultado? ¿Debe documentarse como un cambio importante?

Tenga en cuenta que no hay ninguna propuesta para hacer que string_constant_to_UTF8_byte_representation_conversion no esté disponible cuando la versión del lenguaje C#10 sea el objetivo. En ese caso, el ejemplo anterior pasa a ser un error en lugar de volver a cómo funcionaba en C#10. Esto respeta el principio general de que la versión del lenguaje de destino no afecta a la semántica del lenguaje.

¿Aceptamos este resultado? ¿Debe documentarse como un cambio importante?

La nueva regla tampoco va a evitar roturas que impliquen conversiones literales de tupla. Por ejemplo,

class C
{
    static void Main()
    {
        System.Console.Write(Test(("s", 1)));
    }

    static string Test((object, int) a) => "object";
    static string Test((byte[], int) a) => "array";
}

va a imprimir silenciosamente "matriz" en lugar de "objeto".

¿Aceptamos este resultado? ¿Debe documentarse como un cambio importante? Quizás podríamos complicar la nueva regla para indagar en las conversiones de literales de tupla.

Solución:

El prototipo no ajustará ninguna regla aquí, por lo que es de esperar que podamos ver lo que se rompe en la práctica - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.

(Resuelto) ¿Debería el sufijo u8 no distinguir entre mayúsculas y minúsculas?

Propuesta:

Admite también el sufijo U8 para la coherencia con los sufijos numéricos.

Solución:

Aprobado - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#suffix-case-sensitivity.

Ejemplos actuales

Ejemplos actuales de dónde el tiempo de ejecución ha codificado manualmente los bytes UTF8

Ejemplos en los que dejamos perf sobre la mesa

Reuniones de diseño

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-04-18.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-06-06.md