Letterali di stringhe Utf8
Nota
Questo articolo è una specifica di funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.
Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono registrate nelle pertinenti note della riunione di progettazione del linguaggio (LDM) .
Puoi trovare maggiori informazioni sul processo di adozione delle specifiche delle funzionalità nello standard del linguaggio C# nell'articolo sulle specifiche di .
Sommario
Questa proposta aggiunge la possibilità di scrivere valori letterali stringa UTF8 in C# e di codificarli automaticamente nella rappresentazione UTF-8 byte
.
Motivazione
UTF8 è il linguaggio del Web e il relativo uso è necessario in parti significative dello stack .NET. Anche se gran parte dei dati arriva sotto forma di byte[]
dallo stack di rete, nel codice è ancora presente un uso significativo delle costanti. Ad esempio, lo stack di rete deve scrivere in genere costanti come "HTTP/1.0\r\n"
, " AUTH"
o .
"Content-Length: "
.
Attualmente non esiste una sintassi efficiente per eseguire questa operazione perché C# rappresenta tutte le stringhe che usano la codifica UTF16. Ciò significa che gli sviluppatori devono scegliere tra la praticità della codifica in fase di esecuzione, che comporta un sovraccarico, incluso il tempo impiegato all'avvio per eseguire effettivamente l'operazione di codifica (e allocazioni se il tipo di destinazione non le richiede effettivamente), o la traduzione manuale dei byte e la memorizzazione in 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 "));
Questo trade-off rappresenta una criticità che si presenta frequentemente per i nostri partner nei contesti runtime, ASP.NET e Azure. Spesso fa sì che lascino le prestazioni sulla tabella perché non vogliono superare i problemi di scrittura manuale della codifica byte[]
.
Per risolvere questo problema, permetteremo i valori letterali UTF8 nel linguaggio e li codificheremo nel byte[]
UTF8 durante la compilazione.
Progettazione dettagliata
u8
suffisso sui valori letterali stringa
Il linguaggio fornirà il suffisso u8
sui letterali di stringa per imporre il tipo UTF8.
Il suffisso non fa distinzione tra maiuscole e minuscole, U8
suffisso sarà supportato e avrà lo stesso significato di u8
suffisso.
Quando viene usato il suffisso u8
, il valore letterale è un ReadOnlySpan<byte>
contenente una rappresentazione di byte UTF-8 della stringa.
Un terminatore nullo viene posizionato oltre l'ultimo byte in memoria (e al di fuori dell'area specificata dal ReadOnlySpan<byte>
) per gestire alcuni scenari di interoperabilità in cui la chiamata prevede stringhe terminate con 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>'.
Poiché i valori letterali vengono allocati come costanti globali, la durata dell'ReadOnlySpan<byte>
risultante non impedisce che venga restituita o passata in un'altra posizione. Tuttavia, in alcuni contesti, in particolare nelle funzioni asincrone, non sono consentite variabili locali di tipi di ref struct, quindi ci sarebbe una penalità di utilizzo in tali situazioni, con una chiamata ToArray()
o simile richiesta.
Un valore letterale u8
non ha un valore costante. Ciò è dovuto al fatto che ReadOnlySpan<byte>
non può essere il tipo di una costante oggi. Se la definizione di const
viene espansa in futuro per prendere in considerazione ReadOnlySpan<byte>
, questo valore deve essere considerato anche una costante. Praticamente, tuttavia, ciò significa che un valore letterale u8
non può essere usato come valore predefinito di un parametro facoltativo.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing"u8) { ... }
Quando il testo di input per il valore letterale è una stringa UTF16 in formato non valido, il linguaggio genererà un errore.
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.
Operatore di addizione
Verrà aggiunto un nuovo punto elenco a §12.10.5 operatore di addizione come indicato di seguito.
Concatenazione della rappresentazione dei byte UTF8:
ReadOnlySpan<byte> operator +(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y);
Questo operatore binario
+
esegue la concatenazione delle sequenze di byte ed è applicabile solo se entrambi gli operandi sono rappresentazioni di byte UTF8 semanticamente. Un operando è semanticamente una rappresentazione di byte UTF8 quando è un valore letteraleu8
, o un valore prodotto dall'operatore di concatenazione della rappresentazione di byte UTF8.Il risultato della concatenazione della rappresentazione dei byte UTF8 è un
ReadOnlySpan<byte>
costituito dai byte dell'operando sinistro seguito dai byte dell'operando destro. Un terminatore nullo viene posizionato oltre l'ultimo byte in memoria (e al di fuori dell'area specificata dalReadOnlySpan<byte>
) per gestire alcuni scenari di interoperabilità in cui la chiamata prevede stringhe terminate con null.
Abbassamento
Il linguaggio ridurrà le stringhe con codifica UTF8 esattamente come se lo sviluppatore avesse digitato il valore letterale byte[]
risultante nel codice. Per esempio:
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.
Ciò significa che tutte le ottimizzazioni applicabili al modulo new byte[] { ... }
verranno applicate anche ai valori letterali utf8. Ciò significa che il punto di chiamata sarà privo di allocazioni poiché C# lo ottimizzerà per essere memorizzato nella sezione .data
del file PE.
Più applicazioni consecutive degli operatori di concatenazione della rappresentazione di byte UTF8 vengono compresse in una singola creazione di ReadOnlySpan<byte>
con matrice di byte contenente la sequenza di byte finale.
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.
Svantaggi
Basarsi sulle API di base
L'implementazione del compilatore userà UTF8Encoding
sia per il rilevamento di stringhe non valide sia per la conversione in byte[]
. Le API esatte dipenderanno probabilmente dal framework di destinazione usato dal compilatore. Ma UTF8Encoding
sarà il cavallo di battaglia dell'implementazione.
Storicamente il compilatore ha evitato di usare le API di runtime per l'elaborazione letterale. Ciò è dovuto al fatto che prende il controllo dell'elaborazione delle costanti dal linguaggio al runtime. In concreto significa che elementi come correzioni di bug possono modificare la codifica costante e significa che il risultato della compilazione C# dipende dal runtime in cui viene eseguito il compilatore.
Questo non è un problema ipotetico. Le versioni precedenti di Roslyn usavano double.Parse
per gestire l'analisi delle costanti a virgola mobile. Ciò ha causato un certo numero di problemi. In primo luogo, alcuni valori a virgola mobile avevano rappresentazioni diverse tra il compilatore nativo e Roslyn. In secondo luogo, man mano che .NET Core si è evoluto e ha corretto bug di lunga durata nel codice double.Parse
, il che significava che il significato di tali costanti è cambiato nel linguaggio a seconda del runtime su cui è stato eseguito il compilatore. Di conseguenza, il compilatore ha quindi scritto la propria versione del codice di analisi a virgola mobile e ha rimosso la dipendenza da double.Parse
.
Questo scenario è stato discusso con il team di runtime e non si ritiene che si verifichino gli stessi problemi riscontrati in precedenza. L'analisi UTF8 è stabile tra i runtime e non esistono problemi noti in quest'area che possano influire sulla compatibilità futura. Se si verifica un problema, è possibile rivalutare la strategia.
Alternative
Solo tipo di destinazione
La progettazione può basarsi solo sulla digitazione di destinazione e rimuovere il suffisso u8
sui valori letterali string
. Nella maggior parte dei casi oggi il valore letterale string
viene assegnato direttamente a un ReadOnlySpan<byte>
di conseguenza non è necessario.
ReadOnlySpan<byte> span = "Hello World;"
Il suffisso u8
esiste principalmente per supportare due scenari: var
e overload resolution. Per quest'ultima si consideri il caso d'uso seguente:
void Write(ReadOnlySpan<byte> span) { ... }
void Write(string s) {
var bytes = Encoding.UTF8.GetBytes(s);
Write(bytes.AsSpan());
}
Considerata l'implementazione, è meglio chiamare Write(ReadOnlySpan<byte>)
poiché il suffisso u8
semplifica questa operazione: Write("hello"u8)
. Poiché manca, gli sviluppatori devono ricorrere a cast scomodi Write((ReadOnlySpan<byte>)"hello")
.
Si tratta comunque di un elemento di comodità, la funzionalità può esistere senza di esso e la sua aggiunta in un secondo momento non causa alcuna interruzione.
Attendere il tipo Utf8String
Mentre l'ecosistema .NET si sta standardizzando su ReadOnlySpan<byte>
come tipo di stringa UTF8 de facto, oggi è possibile che in futuro il runtime introduca un tipo effettivo Utf8String
.
Dovremmo valutare la nostra progettazione di fronte a questo possibile cambiamento e riflettere sul fatto se ci pentiremo delle decisioni prese. Quest'ultimo dovrebbe essere ponderato rispetto alla probabilità realistica che verrà introdotto Utf8String
, una probabilità che sembra diminuire ogni giorno in cui troviamo ReadOnlySpan<byte>
come alternativa accettabile.
È improbabile che ci pentiremmo della conversione del tipo di destinazione tra valori letterali stringa e ReadOnlySpan<byte>
. L'uso di ReadOnlySpan<byte>
come utf8 è incorporato nelle API ora e, quindi, c'è ancora valore nella conversione anche se Utf8String
arriva ed è un tipo "migliore". Il linguaggio potrebbe semplicemente preferire le conversioni a Utf8String
rispetto a ReadOnlySpan<byte>
.
Sembra più probabile che ci pentiremmo del suffisso u8
che punta a ReadOnlySpan<byte>
invece di Utf8String
. Sarebbe simile a come ci dispiace che stackalloc int[]
abbia un tipo naturale del int*
invece di Span<int>
. Questo non è un deal breaker, ma solo un inconveniente.
Conversioni tra costanti string
e sequenze di byte
Le conversioni in questa sezione non sono state implementate. Queste conversioni rimangono proposte attive.
Il linguaggio consentirà conversioni tra costanti string
e sequenze byte
in cui il testo viene convertito nella rappresentazione equivalente di byte UTF8. In particolare, il compilatore consentirà string_constant_to_UTF8_byte_representation_conversion: conversioni implicite da costanti string
alle rappresentazioni byte[]
, Span<byte>
e ReadOnlySpan<byte>
.
Verrà aggiunto un nuovo punto elenco alla sezione delle conversioni implicite §10.2. Questa conversione non è una conversione standard §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 il testo di input per la conversione è una stringa UTF16 in formato non valido, la lingua genererà un errore:
const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16
L'utilizzo predominante di questa funzionalità dovrebbe essere con valori letterali, ma funzionerà con qualsiasi valore costante string
.
Verrà supportata anche una conversione da una costante string
di valore null
. Il risultato della conversione sarà il valore default
del tipo di destinazione.
const string data = "dog"
ReadOnlySpan<byte> span = data; // new byte[] { 0x64, 0x6f, 0x67 }
Nel caso di qualsiasi operazione costante sulle stringhe, ad esempio +
, la codifica in UTF8 avverrà sul string
finale anziché avvenire per le singole parti e quindi concatenare i risultati. Questo ordinamento è importante da considerare perché può influire sul fatto che la conversione abbia esito positivo o negativo.
const string first = "\uD83D"; // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;
Le due parti qui non sono valide autonomamente perché sono parti incomplete di una coppia di surrogati. Singolarmente non esiste una traduzione corretta in UTF8, ma insieme formano una coppia surrogata completa che può essere convertita correttamente in UTF8.
Il string_constant_to_UTF8_byte_representation_conversion non è consentito negli alberi di espressione Linq.
Mentre gli input di queste conversioni sono costanti e i dati sono completamente codificati in fase di compilazione, la conversione viene non considerata costante dal linguaggio. Ciò è dovuto al fatto che le matrici non sono costanti oggi. Se la definizione di const
viene espansa in futuro per prendere in considerazione le matrici, è necessario considerare anche queste conversioni. In pratica, ciò significa che un risultato di queste conversioni non può essere usato come valore predefinito di un parametro facoltativo.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... }
Una volta implementati, i valori letterali stringa avranno gli stessi problemi che altri valori letterali hanno nella lingua: il tipo che rappresentano dipende dalla modalità di utilizzo. C# fornisce un suffisso letterale per disambiguare il significato per altri valori letterali. Ad esempio, gli sviluppatori possono scrivere 3.14f
per forzare il valore a essere float
o 1l
per forzare il valore a essere long
.
Domande non risolte
Le prime tre domande di progettazione si riferiscono alla conversione della stringa a Span<byte>
/ ReadOnlySpan<byte>
. Non è stata implementata.
(Risolto) Conversioni tra una costante string
con null
valore e sequenze di byte
Non è specificato se questa conversione sia supportata e, in caso affermativo, come venga eseguita.
Proposta:
Consentire conversioni implicite da una costante string
con valore null
a byte[]
, Span<byte>
e ReadOnlySpan<byte>
. Il risultato della conversione è il valore default
del tipo di destinazione.
La risoluzione :
La proposta è approvata - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.
(Risolto) Dove appartiene string_constant_to_UTF8_byte_representation_conversion?
È string_constant_to_UTF8_byte_representation_conversion un punto puntato nelle conversioni implicite sezione di §10.2 o fa parte di §10.2.11o appartiene ad altri gruppi di conversioni implicite esistenti?
Proposta:
Si tratta di un nuovo punto elenco nelle conversioni implicite §10.2, paragonabile a "Conversioni implicite di stringhe interpolate" oppure "Conversioni di gruppi di metodi". Non sembra appartenere a "Conversioni di espressioni costanti implicite" perché, anche se l'origine è un'espressione costante, il risultato non è mai un'espressione costante. Inoltre, "Conversioni implicite di espressioni costanti" sono considerate "conversioni implicite standard" §10.4.2, che è probabile che comporti modifiche di comportamento non semplici che coinvolgono conversioni definite dall'utente.
La risoluzione :
Verrà introdotto un nuovo tipo di conversione per la stringa costante in byte UTF-8: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds
(Risolto) È string_constant_to_UTF8_byte_representation_conversion la conversione standard?
Oltre alle "pure" conversioni standard (le conversioni standard sono quelle predefinite che possono verificarsi come parte di una conversione definita dall'utente), il compilatore considera anche alcune conversioni predefinite come "un po'" standard. Ad esempio, una conversione implicita di stringhe interpolata può essere eseguita come parte di una conversione definita dall'utente se è presente un cast esplicito al tipo di destinazione nel codice. Come se fosse una conversione esplicita standard, anche se si tratta di una conversione implicita non inclusa in modo esplicito nel set di conversioni implicite o esplicite standard. Per esempio:
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:
La nuova conversione non è una conversione standard. In questo modo si evitano modifiche di comportamento non semplici che coinvolgono conversioni definite dall'utente. Ad esempio, non sarà necessario preoccuparsi delle conversioni definite dall'utente nelle conversioni implicite di letterali di tuple, ecc.
La risoluzione :
Non è una conversione standard per il momento - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.
(Risolto) Conversione dell'albero delle espressioni Linq
È string_constant_to_UTF8_byte_representation_conversion consentito nel contesto di una conversione dell'albero delle espressioni di Linq? Possiamo non consentirlo per il momento o semplicemente includere la forma "ridotta" nell'albero. Per esempio:
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})
Che ne dici dei valori letterali di stringa con suffisso u8
? È possibile visualizzare quelli come creazioni di matrici di byte:
Expression<Func<byte[]>> x = () => "hello"u8; // () => new [] {104, 101, 108, 108, 111}
La risoluzione :
Non consentire negli alberi delle espressioni LINQ - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.
(Risolto) Il tipo naturale di un letterale di tipo stringa con suffisso u8
La sezione "Progettazione dettagliata" indica: "Il tipo naturale sarà tuttavia ReadOnlySpan<byte>
". Allo stesso tempo: "Quando viene usato il suffisso u8
, il valore letterale può comunque essere convertito in uno qualsiasi dei tipi consentiti: byte[]
, Span<byte>
o ReadOnlySpan<byte>
".
Questo approccio presenta diversi svantaggi:
-
ReadOnlySpan<byte>
non è disponibile nel framework desktop; - Non esistono conversioni esistenti da
ReadOnlySpan<byte>
abyte[]
oSpan<byte>
. Per supportarli, è probabile che sia necessario trattare i valori letterali con il tipo della destinazione. Sia le regole del linguaggio che l'implementazione diventeranno più complicate.
Proposta:
Il tipo naturale sarà byte[]
. È facilmente disponibile in tutti i framework. A proposito, durante l'esecuzione inizieremo sempre con la creazione di un array di byte, anche con la proposta originale. Non sono inoltre necessarie regole di conversione speciali per supportare le conversioni in Span<byte>
e ReadOnlySpan<byte>
. Esistono già conversioni implicite definite dall'utente da byte[]
a Span<byte>
e ReadOnlySpan<byte>
. Esiste anche una conversione implicita definita dall'utente verso ReadOnlyMemory<byte>
(consultare di seguito la domanda "Profondità della conversione"). C'è uno svantaggio, il linguaggio non consente il concatenamento delle conversioni definite dall'utente. Di conseguenza, il codice seguente non verrà compilato:
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();
}
Tuttavia, come per qualsiasi conversione definita dall'utente, è possibile usare un cast esplicito per eseguire una conversione definita dall'utente come parte di un'altra conversione definita dall'utente.
Si ha l'impressione che tutti gli scenari motivanti vengano affrontati con byte[]
come tipo naturale, ma le regole del linguaggio e l'implementazione risulteranno notevolmente più semplici.
La risoluzione :
La proposta è approvata - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals.
Probabilmente dovremmo avere un dibattito più approfondito sul fatto che u8
valori letterali stringa debbano avere un tipo di matrice modificabile, ma non pensiamo che il dibattito sia necessario per il momento.
È stato implementato solo l'operatore di conversione esplicito.
(Risolto) Profondità della conversione
Funzionerà anche ovunque un byte[] possa funzionare? Considerare:
static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";
Il primo esempio probabilmente funzionerà grazie al tipo naturale derivante da u8
.
Il secondo esempio è difficile da eseguire perché richiede conversioni in entrambe le direzioni. A meno che non si aggiunga ReadOnlyMemory<byte>
come uno dei tipi di conversione consentiti.
Proposta:
Non fare niente di speciale.
La risoluzione :
Non sono state aggiunte nuove destinazioni di conversione per il momento https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Nessuna delle due conversioni viene compilata.
(Risolto) Errori di risoluzione dei sovraccarichi
L'API seguente diventa ambigua:
M("");
static void M1(ReadOnlySpan<char> charArray) => ...;
static void M1(byte[] byteArray) => ...;
Cosa dobbiamo fare per risolvere questo problema?
Proposta:
Analogamente a https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, il membro della funzione Better (§11.6.4.3) viene aggiornato per preferire i membri in cui nessuna delle conversioni coinvolte richiede di convertire le costanti string
in sequenze UTF8 byte
.
Membro di funzione migliore
... Dato un elenco di argomenti
A
con un set di espressioni di argomento{E1, E2, ..., En}
e due membri di funzione applicabiliMp
eMq
con tipi di parametro{P1, P2, ..., Pn}
e{Q1, Q2, ..., Qn}
,Mp
è definito come membro di funzione migliore rispetto aMq
se
- per ogni argomento, la conversione implicita da
Ex
aPx
non è un string_constant_to_UTF8_byte_representation_conversione per almeno un argomento, la conversione implicita daEx
aQx
è un string_constant_to_UTF8_byte_representation_conversiono- per ogni argomento, la conversione implicita da
Ex
aPx
non è una conversione_di_tipo_di_funzionee
Mp
è un metodo non generico oMp
è un metodo generico con parametri di tipo{X1, X2, ..., Xp}
e per ogni parametro di tipoXi
l'argomento di tipo viene dedotto da un'espressione o da un tipo diverso da un function_typee- per almeno un argomento, la conversione implicita da
Ex
aQx
è un function_type_conversionoMq
è un metodo generico con parametri di tipo{Y1, Y2, ..., Yq}
e per almeno un parametro di tipoYi
l'argomento di tipo viene dedotto da un function_typeo- per ogni argomento, la conversione implicita da
Ex
aQx
non è migliore della conversione implicita daEx
aPx
e per almeno un argomento, la conversione daEx
aPx
è migliore della conversione daEx
aQx
.
Si noti che l'aggiunta di questa regola non coprirà gli scenari in cui i metodi di istanza diventano applicabili e "mettono in ombra" i metodi di estensione. Per esempio:
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";
}
Il comportamento di questo codice cambierà silenziosamente dalla stampa di "stringa" a quella di "byte[]".
Siamo d'accordo con questo cambiamento di comportamento? Deve essere documentato come modifica importante?
Si noti che non c'è alcuna proposta per rendere string_constant_to_UTF8_byte_representation_conversion non disponibile quando si utilizza la versione del linguaggio C#10. In tal caso, l'esempio precedente diventa un errore anziché tornare al comportamento C#10. Questo segue un principio generale che la versione del linguaggio di destinazione non influisce sulla semantica del linguaggio.
Siamo d'accordo con questo comportamento? Deve essere documentato come modifica importante?
La nuova regola non impedirà inoltre interruzioni che coinvolgono conversioni letterali di tupla. Per esempio
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";
}
sta per stampare in modo invisibile all'utente "matrice" anziché "oggetto".
Siamo d'accordo con questo comportamento? Deve essere documentato come modifica importante? Forse potremmo complicare la nuova regola per esaminare le conversioni letterali della tupla.
La risoluzione :
Il prototipo non modificherà alcuna regola qui, quindi possiamo sperare di vedere cosa si rompe in pratica - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.
(Risolto) Il suffisso u8
deve essere senza distinzione tra maiuscole e minuscole?
Proposta:
Supporta il suffisso U8
per coerenza con i suffissi numerici.
La risoluzione :
Approvato: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#suffix-case-sensitivity.
Esempi odierni
Esempi di dove il runtime ha codificato manualmente i byte UTF8 oggi
- 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
Esempi in cui si lasciano le prestazioni nella tabella
- 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
Riunioni di progettazione
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