Literais de cadeia de caracteres UTF-8
Nota
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).
Você pode saber mais sobre o processo de adoção de speclets de recursos no padrão de linguagem C# no artigo sobre as especificações de .
Resumo
Essa proposta adiciona a capacidade de escrever literais de cadeia de caracteres UTF8 em C# e codificá-los automaticamente na representação UTF-8 byte
.
Motivação
UTF8 é a codificação da web e seu uso é necessário em partes significativas da plataforma .NET. Embora grande parte dos dados venha na forma de byte[]
fora da pilha de rede, ainda há usos significativos de constantes no código. Por exemplo, a pilha de rede normalmente precisa gravar constantes como "HTTP/1.0\r\n"
, " AUTH"
ou . "Content-Length: "
.
Hoje não há sintaxe eficiente para fazer isso, pois o C# representa todas as cadeias de caracteres usando a codificação UTF16. Isso significa que os desenvolvedores precisam escolher entre a praticidade de codificar em tempo de execução, com sobrecarga, incluindo o tempo consumido na inicialização ao executar a operação de codificação (e alocações, caso direcione a um tipo que não as requer), ou fazer a tradução manual dos bytes e armazená-los em um 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 "));
Essa compensação é um problema que aparece com frequência para nossos parceiros no tempo de execução, no ASP.NET e no Azure. Muitas vezes, isso faz com que eles não aproveitem todo o potencial porque não querem passar pelo incômodo de escrever a codificação byte[]
manualmente.
Para corrigir isso, permitiremos literais UTF8 na linguagem e os codificaremos no UTF8 byte[]
no tempo de compilação.
Design detalhado
Sufixo u8
em literais de cadeia de caracteres
A linguagem fornecerá o sufixo u8
em literais de cadeia de caracteres para forçar o tipo a ser UTF-8.
O sufixo é insensível a maiúsculas e minúsculas, o sufixo U8
terá suporte e o mesmo significado que o sufixo u8
.
Quando o sufixo u8
é usado, o valor do literal é um ReadOnlySpan<byte>
que contém uma representação de bytes UTF-8 da cadeia de caracteres.
Um terminador nulo é colocado além do último byte na memória (e fora do comprimento da ReadOnlySpan<byte>
) para lidar com alguns cenários de interoperabilidade em que a chamada espera cadeias de caracteres terminadas nulas.
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>'.
Como os literais seriam alocados como constantes globais, a vida útil do ReadOnlySpan<byte>
resultante não impediria que fosse retornado ou transmitido para diferentes contextos. No entanto, determinados contextos, principalmente em funções assíncronas, não permitem variáveis locais de tipos de estrutura de referência, portanto, haveria uma penalidade de uso nessas situações, com uma chamada ToArray()
ou semelhante sendo necessária.
Um literal u8
não tem um valor constante. Isso ocorre porque ReadOnlySpan<byte>
não pode ser o tipo de constante atualmente. Se a definição de const
for expandida no futuro para considerar ReadOnlySpan<byte>
, esse valor também deverá ser considerado uma constante. Praticamente, isso significa que um literal u8
não pode ser usado como o valor padrão de um parâmetro opcional.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing"u8) { ... }
Quando o texto de entrada do literal for uma cadeia de caracteres UTF16 malformada, o idioma emitirá um erro:
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 adição
Um novo marcador será adicionado a §12.10.5 Operador de adição da seguinte maneira.
Concatenação de representação de bytes UTF8:
ReadOnlySpan<byte> operator +(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y);
Esse operador binário
+
realiza a concatenação de sequências de bytes e é aplicável somente se ambos os operandos forem representações de bytes UTF-8 semanticamente. Um operando é semanticamente uma representação de bytes UTF8 quando é um valor de um literalu8
ou um valor produzido pelo operador de concatenação de representação de bytes UTF8.O resultado da concatenação da representação de bytes UTF8 é um
ReadOnlySpan<byte>
que consiste nos bytes do operando esquerdo seguidos pelos bytes do operando direito. Um terminador nulo é colocado além do último byte na memória (e fora do comprimento daReadOnlySpan<byte>
) para lidar com alguns cenários de interoperabilidade em que a chamada espera cadeias de caracteres terminadas nulas.
Abaixamento
A linguagem reduzirá as cadeias de caracteres codificadas em UTF8 exatamente como se o desenvolvedor tivesse tipado o literal de byte[]
resultante no código. Por exemplo:
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.
Isso significa que todas as otimizações que se aplicam ao formulário de new byte[] { ... }
também serão aplicadas a literais utf8. Isso significa que o site de chamada será livre de alocação, pois C# otimizará isso para ser armazenado na seção .data
do arquivo PE.
Várias aplicações consecutivas de operadores de concatenação de representação de bytes UTF8 são combinadas em uma única criação de ReadOnlySpan<byte>
com matriz de bytes que contém a sequência 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
Dependendo das APIs principais
A implementação do compilador usará UTF8Encoding
para detecção de cadeia de caracteres inválida, bem como tradução para byte[]
. As APIs exatas possivelmente dependerão de qual estrutura de destino o compilador está usando. Mas UTF8Encoding
será a peça fundamental da implementação.
Historicamente, o compilador tem evitado o uso de APIs de runtime para processamento literal. Isso ocorre porque ele retira do idioma a responsabilidade de processar constantes e a transfere para o runtime. Concretamente, isso significa que itens como correções de bug podem alterar a codificação constante e significam que o resultado da compilação C# depende de qual runtime o compilador está executando.
Este não é um problema hipotético. As primeiras versões do Roslyn usaram double.Parse
para lidar com a análise de constantes de ponto flutuante. Isso causou vários problemas. Primeiro, isso significava que alguns valores de ponto flutuante tinham representações diferentes entre o compilador nativo e Roslyn. Em segundo lugar, à medida que o .NET Core evoluiu e corrigiu bugs de longa data no código double.Parse
, isso significava que o significado dessas constantes foi alterado no idioma, dependendo de qual runtime o compilador executou. Como resultado, o compilador acabou escrevendo sua própria versão do código de análise de ponto flutuante e removendo a dependência em double.Parse
.
Esse cenário foi discutido com a equipe de runtime e não achamos que ele tenha os mesmos problemas que já tivemos antes. A análise UTF8 é estável em todos os ambientes de execução, e não há problemas conhecidos nessa área que possam gerar preocupações futuras de compatibilidade. Caso algo apareça, podemos reavaliar a estratégia.
Alternativas
Somente tipo de destino
O design pode depender apenas da tipagem de destino e remover o sufixo u8
em literais string
. Na maioria dos casos de hoje, o literal string
está sendo atribuído diretamente a um ReadOnlySpan<byte>
, portanto, é desnecessário.
ReadOnlySpan<byte> span = "Hello World;"
O sufixo u8
existe principalmente para dar suporte a dois cenários: var
e resolução de sobrecarga. Para este último, considere o seguinte caso de uso:
void Write(ReadOnlySpan<byte> span) { ... }
void Write(string s) {
var bytes = Encoding.UTF8.GetBytes(s);
Write(bytes.AsSpan());
}
Dada a implementação, é melhor chamar Write(ReadOnlySpan<byte>)
e o sufixo u8
torna isso conveniente: Write("hello"u8)
. Falta de que os desenvolvedores precisam para recorrer à conversão complicada Write((ReadOnlySpan<byte>)"hello")
.
Ainda assim, é um item de praticidade e o recurso pode existir sem ele, e não é essencial adicioná-lo mais tarde.
Aguardar o tipo Utf8String
Embora o ecossistema do .NET esteja padronizando o ReadOnlySpan<byte>
como o tipo de cadeia de caracteres Utf8 de fato hoje, é possível que o tempo de execução introduza um tipo real de Utf8String
no futuro.
Devemos avaliar nosso design aqui diante dessa possível mudança e refletir sobre se nos arrependeríamos das decisões que tomamos. Isso deve ser ponderado contra a probabilidade realista de introduzirmos Utf8String
, uma probabilidade que parece diminuir a cada dia que encontramos ReadOnlySpan<byte>
como uma alternativa aceitável.
Parece improvável que nos arrependamos da conversão de tipo de destino entre literais de cadeia de caracteres e ReadOnlySpan<byte>
. O uso de ReadOnlySpan<byte>
como utf8 é inserido em nossas APIs agora e, portanto, ainda há valor na conversão, mesmo que Utf8String
apareça e seja um tipo "melhor". O idioma poderia simplesmente preferir conversões para Utf8String
em vez de ReadOnlySpan<byte>
.
Parece mais provável que nos arrependamos do sufixo u8
apontando para ReadOnlySpan<byte>
em vez de Utf8String
. Seria semelhante ao modo como lamentamos que stackalloc int[]
tenha um tipo natural de int*
em vez de Span<int>
. Isso não é um impeditivo, apenas um inconveniente.
Conversões entre constantes string
e sequências de byte
As conversões nesta seção não foram implementadas. Essas conversões permanecem propostas ativas.
O idioma permitirá conversões entre constantes string
e sequências byte
, nas quais o texto é convertido na representação de bytes UTF-8 equivalente. Especificamente, o compilador permitirá string_constant_to_UTF8_byte_representation_conversion – conversões implícitas de constantes string
em byte[]
, Span<byte>
e ReadOnlySpan<byte>
.
Um novo marcador será adicionado à seção de conversões implícitas, §10.2. Essa conversão não é uma conversão padrão §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 }
Quando o texto de entrada da conversão for uma cadeia de caracteres UTF16 malformada, o idioma emitirá um erro:
const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16
Espera-se que o uso predominante desse recurso seja com literais, mas funcionará com qualquer valor constante string
.
A conversão de uma constante string
com valor null
também terá suporte. O resultado da conversão será o valor default
do tipo de destino.
const string data = "dog"
ReadOnlySpan<byte> span = data; // new byte[] { 0x64, 0x6f, 0x67 }
No caso de qualquer operação constante em cadeias de caracteres, como +
, a codificação para UTF8 ocorrerá no string
final, em vez de acontecer para as partes individuais e depois concatenar os resultados. Essa ordenação é importante considerar porque pode afetar se a conversão é bem-sucedida ou não.
const string first = "\uD83D"; // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;
As duas partes aqui são inválidas por conta própria, pois são partes incompletas de um par substituto. Individualmente, não há tradução correta para UTF8, mas juntos eles formam um par substituto completo que pode ser traduzido com êxito para UTF8.
A string_constant_to_UTF8_byte_representation_conversion não é permitida em árvores de expressão Linq.
Embora as entradas para essas conversões sejam constantes e os dados sejam totalmente codificados em tempo de compilação, a conversão não é considerada constante pela linguagem. Isso ocorre porque as matrizes não são constantes hoje. Se a definição de const
for expandida no futuro para considerar matrizes, essas conversões também deverão ser consideradas. Praticamente, embora isso signifique que um resultado dessas conversões não pode ser usado como o valor padrão de um parâmetro opcional.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... }
Uma vez que literais de string sejam implementados, eles terão o mesmo problema que outros literais na linguagem: o tipo que representam depende de como são usados. C# fornece um sufixo de literal para desambiguar o significado para outros literais. Por exemplo, os desenvolvedores podem escrever 3.14f
para forçar o valor a ser um float
ou 1l
para forçar o valor a ser um long
.
Perguntas não resolvidas
As três primeiras perguntas de design estão relacionadas às conversões de cadeia de caracteres em Span<byte>
/ ReadOnlySpan<byte>
. Elas não foram implementadas.
(Resolvido) Conversões entre uma constante string
de valor null
e sequências de byte
Se essa conversão tem suporte e, em caso afirmativo, como ela é executada não é especificada.
Proposta:
Permitir conversões implícitas de uma constante string
com valor null
para byte[]
, Span<byte>
e ReadOnlySpan<byte>
. O resultado da conversão é o valor default
do tipo de destino.
Resolução:
A proposta está aprovada - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.
(Resolvido) Ao que string_constant_to_UTF8_byte_representation_conversion pertence?
A string_constant_to_UTF8_byte_representation_conversion é um marcador na seção de conversões implícitas §10.2 por si só, faz parte de §10.2.11 ou pertence a algum outro grupo de conversões implícitas existente?
Proposta:
É um novo marcador em conversões implícitas §10.2, semelhante a "Conversões de cadeia de caracteres interpoladas implícitas" ou "Conversões de grupo de métodos". Ele não parece pertencer a "Conversões de expressões constantes implícitas" porque, mesmo que a origem seja uma expressão constante, o resultado nunca é uma expressão constante. Além disso, "Conversões de expressão constante implícita" são consideradas "Conversões implícitas padrão" §10.4.2, o que provavelmente levará a alterações de comportamento não triviais envolvendo conversões definidas pelo usuário.
Resolução:
Apresentaremos um novo tipo de conversão para constante de cadeia de caracteres para bytes UTF-8 – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds
(Resolvido) A string_constant_to_UTF8_byte_representation_conversion é uma conversão padrão
Além das Conversões Padrão "puras" (as conversões padrão são aquelas conversões predefinidas que podem ocorrer como parte de uma conversão definida pelo usuário), o compilador também trata algumas conversões predefinidas como padrão "um pouco". Por exemplo, uma conversão de cadeia de caracteres interpolada implícita pode ocorrer como parte de uma conversão definida pelo usuário se houver uma conversão explícita para o tipo de destino no código. Como se fosse uma conversão explícita padrão, embora seja uma conversão implícita que não está explicitamente incluída no conjunto padrão de conversões implícitas ou explícitas. Por exemplo:
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();
}
Proposta:
A nova conversão não é uma conversão padrão. Isso evitará alterações de comportamento não triviais envolvendo conversões definidas pelo usuário. Por exemplo, não precisaremos nos preocupar com as conversões definidas pelo usuário em conversões literais de tupla implícita etc.
Resolução:
Não é uma conversão padrão, por enquanto - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.
(Resolvido) Conversão de árvore de expressão Linq
A string_constant_to_UTF8_byte_representation_conversion deve ser permitida no contexto de uma conversão de árvore de expressão Linq? Podemos impedir isso por enquanto, ou podemos simplesmente incluir a forma "reduzida" na árvore. Por exemplo:
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})
E quanto a literais de cadeia de caracteres com sufixo u8
? Poderíamos exibi-las como criações de matriz de bytes:
Expression<Func<byte[]>> x = () => "hello"u8; // () => new [] {104, 101, 108, 108, 111}
Resolução:
Não permita em árvores de expressão Linq – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.
(Resolvido) O tipo natural de um valor literal de cadeia de caracteres com sufixo u8
A seção "Design detalhado" diz: "O tipo natural, porém, será ReadOnlySpan<byte>
." Ao mesmo tempo: "Quando o sufixo u8
é usado, o literal ainda pode ser convertido em qualquer um dos tipos permitidos: byte[]
, Span<byte>
ou ReadOnlySpan<byte>
."
Há várias desvantagens com essa abordagem:
ReadOnlySpan<byte>
não está disponível no framework de desktop;- Não há conversões existentes de
ReadOnlySpan<byte>
parabyte[]
ouSpan<byte>
. Para dar suporte, provavelmente precisaremos tratar os literais como destino tipado. As regras de idioma e a implementação se tornarão mais complicadas.
Proposta:
O tipo natural será byte[]
. Está facilmente disponível em todas as estruturas. A propósito, em tempo de execução, sempre começaremos criando uma matriz de bytes, mesmo com a proposta original. Também não precisamos de regras de conversão especiais para dar suporte a conversões em Span<byte>
e ReadOnlySpan<byte>
. Já existem conversões implícitas definidas pelo usuário de byte[]
para Span<byte>
e ReadOnlySpan<byte>
. Há até uma conversão implícita definida pelo usuário para ReadOnlyMemory<byte>
(consulte a pergunta "Profundidade da conversão" abaixo). Há uma desvantagem, o idioma não permite o encadeamento de conversões definidas pelo usuário. Portanto, o código a seguir não será compilado:
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();
}
No entanto, como em qualquer conversão definida pelo usuário, uma conversão explícita pode ser usada para integrar uma conversão definida pelo usuário como parte de outra.
Parece que todos os cenários motivadores serão abordados com byte[]
como o tipo natural, mas as regras de linguagem e a implementação serão significativamente mais simples.
Resolução:
A proposta está aprovada - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals.
Provavelmente queremos ter um debate mais profundo sobre se os literais de cadeia de caracteres u8
devem ter um tipo de matriz mutável, mas não achamos que esse debate seja necessário por enquanto.
Somente o operador de conversão explícita foi implementado.
(Resolvido) Profundidade da conversão
Funcionará também em qualquer lugar em que um byte[] possa funcionar? Considere:
static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";
O primeiro exemplo provavelmente deve funcionar devido ao tipo natural proveniente de u8
.
O segundo exemplo é difícil de fazer funcionar porque requer conversões nas duas direções. Isso ocorre a menos que adicionemos ReadOnlyMemory<byte>
como um dos tipos de conversão permitidos.
Proposta:
Não faça nada especial.
Resolução:
Não há novos destinos de conversão adicionados por enquanto https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Nenhuma conversão é compilada.
(Resolvido) Falhas na resolução de sobrecarga
A API a seguir se tornaria ambígua:
M("");
static void M1(ReadOnlySpan<char> charArray) => ...;
static void M1(byte[] byteArray) => ...;
O que devemos fazer para resolver isso?
Proposta:
Semelhante ao https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, o membro de função Better (§11.6.4.3) é atualizado para preferir membros onde nenhuma das conversões envolvidas exija converter constantes string
em sequências UTF8 byte
.
Membro de função Better
... Dada uma lista de argumentos
A
com um conjunto de expressões de argumento{E1, E2, ..., En}
e dois membros de função aplicáveisMp
eMq
com tipos de parâmetro{P1, P2, ..., Pn}
e{Q1, Q2, ..., Qn}
,Mp
é definido como um membro de função melhor do queMq
se
- para cada argumento, a conversão implícita de
Ex
emPx
não é uma string_constant_to_UTF8_byte_representation_conversion e, para pelo menos um argumento, a conversão implícita deEx
emQx
é uma string_constant_to_UTF8_byte_representation_conversion ou- para cada argumento, a conversão implícita de
Ex
emPx
não é uma function_type_conversion e
Mp
é um método não genérico ouMp
é um método genérico com parâmetros de tipo{X1, X2, ..., Xp}
, e para cada parâmetro de tipoXi
, o argumento de tipo é inferido de uma expressão ou de um tipo que não seja um function_type e- para pelo menos um argumento, a conversão implícita de
Ex
emQx
é uma function_type_conversion ouMq
é um método genérico com parâmetros de tipo{Y1, Y2, ..., Yq}
e, para pelo menos um parâmetro de tipoYi
, o argumento de tipo é inferido de um function_type ou- para cada argumento, a conversão implícita de
Ex
paraQx
não é melhor do que a conversão implícita deEx
paraPx
e, para pelo menos um argumento, a conversão deEx
paraPx
é melhor do que a conversão deEx
paraQx
.
A adição dessa regra não abordará cenários com métodos de instância se tornando aplicáveis e métodos de extensão de "sombreamento". Por exemplo:
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";
}
O comportamento desse código mudará silenciosamente de imprimir "cadeia de caracteres" para imprimir "byte[]".
Estamos bem com essa mudança de comportamento? Deve ser documentado como uma alteração significativa?
Não há nenhuma proposta para tornar string_constant_to_UTF8_byte_representation_conversion indisponível quando a versão da linguagem C#10 for direcionada. Nesse caso, o exemplo acima se torna um erro em vez de retornar ao comportamento do C#10. Isso segue um princípio geral de que a versão da linguagem de destino não afeta a semântica do idioma.
Estamos bem com esse comportamento? Deve ser documentado como uma alteração significativa?
A nova regra também não impedirá quebras envolvendo conversões literais de tupla. Por exemplo
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";
}
vai imprimir silenciosamente "matriz" em vez de "objeto".
Estamos bem com esse comportamento? Deve ser documentado como uma alteração significativa? Talvez possamos tornar a nova regra mais complexa para se aprofundar nas conversões de literais de tupla.
Resolução:
O protótipo não ajustará nenhuma regra aqui, então, felizmente, podemos ver o que quebra na prática - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.
(Resolvido) O sufixo u8
deve diferenciar maiúsculas de minúsculas?
Proposta:
Suporte ao sufixo U8
também para consistência com sufixos numéricos.
Resolução:
Aprovado - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#suffix-case-sensitivity.
Exemplos de hoje
Exemplos de onde o runtime codificou manualmente os bytes UTF8 hoje
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/StatusCodes.cs#L13-L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs#L581-L591
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStream.Windows.cs#L284
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L30
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs#L852
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs#L35-L42
Exemplos em que deixamos o desempenho de lado
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs#L16-L17
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs#L37-L43
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs#L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpCommands.cs#L669-L687
Reuniões de design
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
C# feature specifications