Partager via


Littérales de chaîne Utf8

Remarque

Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Il inclut les modifications de spécification proposées, ainsi que les informations nécessaires pendant la conception et le développement de la fonctionnalité. Ces articles sont publiés jusqu’à ce que les modifications de spécification proposées soient finalisées et incorporées dans la spécification ECMA actuelle.

Il peut y avoir des différences entre la spécification de la fonctionnalité et l’implémentation terminée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).

Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications .

Résumé

Cette proposition ajoute la possibilité d’écrire des littéraux de chaîne UTF8 en C# et de les encoder automatiquement dans leur représentation UTF-8 byte.

Motivation

UTF8 est le langage du web et son utilisation est nécessaire dans des parties significatives de la pile .NET. Bien que la plupart des données se présentent sous la forme de byte[] hors de la pile du réseau, le code utilise encore beaucoup de constantes. Par exemple, la pile réseau doit souvent écrire des constantes comme "HTTP/1.0\r\n", ou " AUTH". "Content-Length: ".

Aujourd’hui, il n’existe aucune syntaxe efficace pour ce faire, car C# représente toutes les chaînes à l’aide de l’encodage UTF16. Cela signifie que les développeurs doivent choisir entre la commodité de l'encodage au moment de l'exécution, ce qui entraîne une surcharge, y compris le temps passé au démarrage pour effectuer réellement l'opération d'encodage (et les allocations dans le cas où le type ciblé ne les nécessite pas réellement), ou de traduire manuellement les octets et de les stocker dans 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 "));

Ce compromis est un point de douleur qui s’affiche fréquemment pour nos partenaires dans le runtime, ASP.NET et Azure. Souvent, cela les amène à négliger la performance parce qu'ils ne veulent pas se donner la peine d'écrire manuellement l'encodage byte[].

Pour résoudre ce problème, nous allons autoriser les littéraux UTF8 dans la langue et les encoder dans le byte[] UTF8 au moment de la compilation.

Conception détaillée

u8 suffixe sur les littérales de chaîne

Le langage fournira le suffixe u8 sur les littérales de chaîne pour forcer le type à être UTF8. Le suffixe est insensible à la casse, le suffixe U8 sera pris en charge et aura la même signification que le suffixe u8.

Lorsque le suffixe u8 est utilisé, la valeur du littéral est une ReadOnlySpan<byte> contenant une représentation en octets UTF-8 de la chaîne. Un terminateur Null est placé au-delà du dernier octet en mémoire (et en dehors de la longueur du ReadOnlySpan<byte>) afin de gérer certains scénarios d’interopérabilité où l’appel attend des chaînes terminées 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>'.

Étant donné que les lettres sont allouées en tant que constantes globales, la durée de vie du ReadOnlySpan<byte> résultant ne l'empêchera pas d'être renvoyé ou transmis à d'autres personnes. Toutefois, certains contextes, notamment dans les fonctions asynchrones, n'autorisent pas les variables locales de types struct ref, ce qui entraînerait une pénalité d'utilisation dans ces cas, nécessitant un appel à ToArray() ou un appel équivalent.

Un littéral u8 n’a pas de valeur constante. C’est parce que ReadOnlySpan<byte> ne peut pas être le type d’une constante aujourd’hui. Si la définition de const est développée à l’avenir pour prendre en compte ReadOnlySpan<byte>, cette valeur doit également être considérée comme une constante. Pratiquement, cela signifie qu’un littéral u8 ne peut pas être utilisé comme valeur par défaut d’un paramètre facultatif.

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

Lorsque le texte d’entrée du littéral est une chaîne UTF16 malformée, le langage émet une erreur :

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.

Opérateur d’ajout

Un nouveau point sera ajouté au §12.10.5 Opérateur d'addition comme suit.

  • Concaténation de représentation d’octets UTF8 :

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

    Cet opérateur de + binaire effectue la concaténation des séquences d’octets et s’applique uniquement si les deux opérandes sont des représentations d’octets UTF8 sémantiquement. Une opérande est sémantiquement une représentation d'octets UTF8 lorsqu'il s'agit d'une valeur issue d'un littéral u8, ou d'une valeur produite par l'opérateur de concaténation de représentation d'octets UTF8.

    Le résultat de la concaténation des octets UTF-8 est un ReadOnlySpan<byte>, qui se compose des octets de l’opérande gauche suivis des octets de l’opérande droit. Un terminateur Null est placé au-delà du dernier octet en mémoire (et en dehors de la longueur du ReadOnlySpan<byte>) afin de gérer certains scénarios d’interopérabilité où l’appel attend des chaînes terminées null.

Abaissement

Le langage abaissera les chaînes encodées en UTF8 exactement comme si le développeur avait tapé le littéral byte[] résultant dans le code. Par exemple:

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.

Cela signifie que toutes les optimisations qui s’appliquent au formulaire new byte[] { ... } s’appliquent également aux littéraux utf8. Cela signifie que le point d'appel sera exempt d'allocation, car C# l'optimisera pour qu'il soit stocké dans la section .data du fichier PE.

Plusieurs applications consécutives d'opérateurs de concaténation de représentation d'octets UTF8 sont regroupées en une seule création de ReadOnlySpan<byte> avec un tableau d'octets contenant la séquence d'octets 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.

Inconvénients

S’appuyer sur des API principales

L’implémentation du compilateur utilise UTF8Encoding pour la détection de chaîne non valide ainsi que la traduction vers byte[]. Les API exactes dépendent éventuellement de l’infrastructure cible utilisée par le compilateur. Mais UTF8Encoding sera le cheval de bataille de la mise en œuvre.

Historiquement, le compilateur a évité d’utiliser des API runtime pour le traitement littéral. Cela est dû au fait qu’il prend le contrôle de la façon dont les constantes sont traitées loin de la langue et dans le runtime. Concrètement, cela signifie que les éléments tels que les correctifs de bogues peuvent modifier l’encodage constant et signifient que le résultat de la compilation C# dépend du runtime sur lequel le compilateur s’exécute.

Ce n’est pas un problème hypothétique. Les premières versions de Roslyn utilisaient double.Parse pour gérer l’analyse des constantes à virgule flottante. Cela a provoqué un certain nombre de problèmes. Tout d’abord, certaines valeurs à virgule flottante avaient des représentations différentes entre le compilateur natif et Roslyn. Deuxièmement, comme .NET Core a évolué et résolu des bogues de longue date dans le code double.Parse, cela signifie que la signification de ces constantes a changé dans le langage en fonction du runtime sur lequel le compilateur s’est exécuté. Par conséquent, le compilateur a fini par écrire sa propre version du code d’analyse à virgule flottante et de supprimer la dépendance sur double.Parse.

Ce scénario a été discuté avec l’équipe d’exécution et nous ne pensons pas qu’il présente les mêmes problèmes que ceux que nous avons rencontrés précédemment. L’analyse UTF8 est stable dans les environnements d'exécution, et il n’y a pas de problèmes connus dans ce domaine qui pourraient poser des problèmes de compatibilité à l'avenir. Si quelque chose survient, nous pouvons réévaluer la stratégie.

Alternatives

Type cible uniquement

La conception peut s’appuyer uniquement sur le typage cible et supprimer le suffixe u8 sur les littéraux string. Dans la majorité des cas aujourd’hui, le littéral string est affecté directement à un ReadOnlySpan<byte> donc il n’est pas nécessaire.

ReadOnlySpan<byte> span = "Hello World;" 

Le suffixe u8 existe principalement pour prendre en charge deux scénarios : var et la résolution de surcharge. Pour ce dernier, tenez compte du cas d’usage suivant :

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

Étant donné l’implémentation, il est préférable d’appeler Write(ReadOnlySpan<byte>) et le suffixe u8 rend cela pratique : Write("hello"u8). Sans cela, les développeurs doivent recourir à un casting maladroit Write((ReadOnlySpan<byte>)"hello").

Il s'agit toujours d'un élément pratique ; la fonctionnalité peut exister sans cet élément et cela ne pose pas de problème de l'ajouter ultérieurement.

Attendre le type Utf8String

Bien que l'écosystème .NET se standardise aujourd'hui sur ReadOnlySpan<byte> en tant que type de chaîne Utf8 de facto, il est possible que le runtime introduise un véritable type Utf8String à l'avenir.

Nous devrions évaluer notre conception ici en face de ce changement possible et réfléchir sur si nous regrettons les décisions que nous avons prises. Cela devrait être pesé contre la probabilité réaliste que nous introduisions Utf8String, une probabilité qui semble diminuer chaque jour où nous trouvons ReadOnlySpan<byte> comme une alternative acceptable.

Il semble peu probable que nous regrettions la conversion de type cible entre les littéraux de chaîne et ReadOnlySpan<byte>. L'utilisation de ReadOnlySpan<byte> comme utf8 est intégrée dans nos API maintenant et, par conséquent, la conversion conserve de la valeur même si Utf8String arrive et est un type « meilleur ». La langue pourrait simplement préférer les conversions à Utf8String sur ReadOnlySpan<byte>.

Il semble plus probable que nous regrettons le suffixe u8 pointant vers ReadOnlySpan<byte> au lieu de Utf8String. Il serait similaire à la façon dont nous regrettons que stackalloc int[] a un type naturel de int* au lieu de Span<int>. Ce n’est pas un facteur décisif cependant, juste un inconvénient.

Conversions entre les constantes string et les séquences de byte

Les conversions de cette section n’ont pas été implémentées. Ces conversions restent des propositions actives.

La langue autorise les conversions entre les constantes string et les séquences byte où le texte est converti en représentation d’octet UTF8 équivalente. Plus précisément, le compilateur autorisera la conversion string_constant_to_UTF8_byte_representation_conversion - conversions implicites de constantes string vers byte[], Span<byte> et ReadOnlySpan<byte>. Une nouvelle puce sera ajoutée à la section §10.2 sur les conversions implicites. Cette conversion n’est pas une conversion 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 }

Lorsque le texte d’entrée de la conversion est une chaîne UTF16 incorrecte, la langue émet une erreur :

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

L’utilisation prédominante de cette fonctionnalité est censée être avec des constantes littérales, mais elle fonctionnera avec n’importe quelle valeur constante de type string. La conversion d'une constante string en une valeur null sera également prise en charge. Le résultat de la conversion sera une valeur default du type cible.

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

Dans le cas d'une opération constante sur des chaînes de caractères, telle que +, l'encodage en UTF8 se fera sur le string final plutôt que sur les parties individuelles et la concaténation des résultats. Cet ordre est important à prendre en compte, car il peut avoir un impact sur la réussite ou non de la conversion.

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

Les deux parties ici ne sont pas valides par eux-mêmes, car elles sont des parties incomplètes d’une paire de substitution. Individuellement, il n’y a pas de traduction correcte vers UTF8, mais ensemble ils forment une paire de substitution complète qui peut être traduite avec succès en UTF8.

La string_constant_to_UTF8_byte_representation_conversion n'est pas autorisée dans les arbres d'expression Linq.

Bien que les entrées de ces conversions soient des constantes et que les données soient entièrement encodées au moment de la compilation, la conversion n'est pas considérée comme constante par le langage. En effet, les tableaux ne sont pas constants aujourd'hui. Si la définition de const est développée ultérieurement pour prendre en compte les tableaux, ces conversions doivent également être prises en compte. Pratiquement, cela signifie qu’un résultat de ces conversions ne peut pas être utilisé comme valeur par défaut d’un paramètre facultatif.

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

Une fois que les littéraux de chaîne implémentés auront le même problème que les autres littéraux dans la langue : le type qu’ils représentent dépend de la façon dont ils sont utilisés. C# fournit un suffixe littéral pour lever toute ambiguïté quant à la signification d'autres littéraux. Par exemple, les développeurs peuvent écrire 3.14f pour forcer la valeur à être un float ou 1l pour forcer la valeur à être un long.

Questions non résolues

Les trois premières questions de conception concernent les conversions de chaînes de caractères en Span<byte> / ReadOnlySpan<byte> et n'ont pas été mises en œuvre.

(Résolu) Conversions entre une constante string ayant la valeur null et des séquences byte

Indique si cette conversion est prise en charge et, le cas échéant, la façon dont elle est effectuée n’est pas spécifiée.

Proposition :

Autoriser les conversions implicites d’une constante string dont la valeur est null en byte[], Span<byte>et ReadOnlySpan<byte>. Le résultat de la conversion est la valeur default du type cible.

Résolution :

La proposition est approuvée - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.

(Résolu) Quelle est la place de string_constant_to_UTF8_byte_representation_conversion?

La string_constant_to_UTF8_byte_representation_conversion est-elle un point de la section conversions implicites §10.2 à part entière, ou fait-elle partie du §10.2.11, ou appartient-elle à un autre groupe de conversions implicites existant ?

Proposition :

Il s'agit d'un nouveau point dans la section conversions implicites §10.2, similaire à « Implicit interpolated string conversions » ou « Method group conversions ». Il n’a pas l’impression qu’il appartient à « Conversions d’expressions constantes implicites » car, même si la source est une expression constante, le résultat n’est jamais une expression constante. En outre, les « conversions d’expressions constantes implicites » sont considérées comme des « conversions implicites standard » §10.4.2, ce qui est susceptible d’entraîner des modifications de comportement non triviales impliquant des conversions définies par l’utilisateur.

Résolution :

Nous allons introduire un nouveau type de conversion pour la constante de chaîne en octets UTF-8 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds

(Résolu) La conversion string_constant_to_UTF8_byte_representation_conversion est-elle une conversion standard ?

Outre les conversions standard « pures » (les conversions standard sont ces conversions prédéfinies qui peuvent se produire dans le cadre d’une conversion définie par l’utilisateur), le compilateur traite également certaines conversions prédéfinies comme « quelque peu » standard. Par exemple, une conversion de chaîne interpolée implicite peut se produire dans le cadre d’une conversion définie par l’utilisateur s’il existe un cast explicite vers le type cible dans le code. Comme s’il s’agit d’une conversion explicite standard, même s’il s’agit d’une conversion implicite non explicitement incluse dans l’ensemble de conversions implicites ou explicites standard. Par exemple:

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();
}

Proposition :

La nouvelle conversion n’est pas une conversion standard. Cela évite les modifications de comportement non triviales impliquant des conversions définies par l’utilisateur. Par exemple, nous n'aurons pas à nous préoccuper des conversions définies par l'utilisateur dans le cadre des conversions implicites de littéraux de tuple, etc.

Résolution :

Pas une conversion standard, pour l’instant - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.

(Résolu) Conversion de l'arbre d'expression Linq

La conversion string_constant_to_UTF8_byte_representation devrait-elle être autorisée dans le contexte d'une conversion Linq Expression Tree ? Nous pouvons le interdire pour l’instant, ou nous pourrions simplement inclure la forme « réduite » dans l’arbre. Par exemple:

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'en est-il des littérales de chaîne avec le suffixe u8? Nous pourrions les présenter comme des créations de tableaux d'octets :

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

Résolution :

Disallow in Linq Expression Trees - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.

(Résolu) Le type naturel d'un littéral de chaîne avec le suffixe u8

La section « Conception détaillée » indique : « Le type naturel sera cependant ReadOnlySpan<byte>». En même temps : « Lorsque le suffixe u8 est utilisé, le littéral peut toujours être converti en l’un des types autorisés : byte[], Span<byte> ou ReadOnlySpan<byte>».

Cette approche présente plusieurs inconvénients :

  • ReadOnlySpan<byte> n’est pas disponible sur l’infrastructure de bureau ;
  • Il n’existe aucune conversion existante de ReadOnlySpan<byte> en byte[] ou Span<byte>. Pour les prendre en charge, nous devrons probablement traiter les littéraux comme des types cibles. Les règles de langage et l’implémentation deviennent plus complexes.

Proposition :

Le type naturel sera byte[]. Il est facilement disponible sur tous les frameworks. En outre, lors de l'exécution, nous commencerons toujours par créer un tableau d'octets, même avec la proposition originale. Nous n’avons pas besoin de règles de conversion spéciales pour prendre en charge les conversions en Span<byte> et ReadOnlySpan<byte>. Il existe déjà des conversions implicites définies par l’utilisateur de byte[] en Span<byte> et ReadOnlySpan<byte>. Il existe même une conversion implicite définie par l’utilisateur en ReadOnlyMemory<byte> (voir la question « Profondeur de la conversion » ci-dessous). Il existe un inconvénient, le langage n’autorise pas la chaînage des conversions définies par l’utilisateur. Par conséquent, le code suivant ne sera pas compilé :

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();
}

Toutefois, comme pour toute conversion définie par l’utilisateur, un cast explicite peut être utilisé pour faire d’une conversion définie par l’utilisateur une partie d’une autre conversion définie par l’utilisateur.

Il semble que tous les scénarios de motivation vont être traités avec byte[] comme type naturel, mais les règles de langage et l’implémentation seront considérablement plus simples.

Résolution :

La proposition est approuvée - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals. Nous aurons probablement un débat plus approfondi sur la question de savoir si les littéraux de chaîne u8 devraient avoir un type de tableau mutable, mais nous ne pensons pas que ce débat soit nécessaire pour l'instant.

Seul l’opérateur de conversion explicite a été implémenté.

(Résolu) Profondeur de la conversion

Va-t-il également fonctionner n’importe où qu’un octet[] pourrait fonctionner ? Considérer:

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

Le premier exemple devrait probablement fonctionner en raison du type naturel qui provient de u8.

Le deuxième exemple est difficile à effectuer, car il nécessite des conversions dans les deux sens. Autrement dit, sauf si nous ajoutons ReadOnlyMemory<byte> en tant qu’un des types de conversion autorisés.

Proposition :

Ne fais rien de spécial.

Résolution :

Aucune nouvelle cible de conversion n’a été ajoutée pour l’instant https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Aucune des deux conversions ne compile.

(Résolu) Rupture de la résolution des surcharges

L’API suivante devient ambiguë :

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

Qu’est-ce qu’il faut faire pour y remédier ?

Proposition :

Comme pour https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, Meilleur membre fonctionnel (§11.6.4.3) est mis à jour pour préférer les membres où aucune des conversions impliquées ne nécessite de convertir les constantes string en séquences UTF8 byte.

Meilleur membre de fonction

... Étant donné une liste d’arguments A avec un ensemble d’expressions d’arguments {E1, E2, ..., En} et deux membres de fonction applicables Mp et Mq avec des types de paramètres {P1, P2, ..., Pn} et {Q1, Q2, ..., Qn}, Mp est défini pour être un membre de fonction meilleur que Mq si

  1. pour chaque argument, la conversion implicite de Ex en Px n'est pas une string_constant_to_UTF8_byte_representation_conversion, et pour au moins un argument, la conversion implicite de Ex en Qx est une string_constant_to_UTF8_byte_representation_conversion, ou
  2. pour chaque argument, la conversion implicite de Ex vers Px n'est pas une function_type_conversion, et
    • Mp est une méthode non générique ou Mp est une méthode générique avec des paramètres de type {X1, X2, ..., Xp} et pour chaque paramètre de type Xi l’argument de type est déduit d’une expression ou d’un type autre qu’un function_type, et
    • pour au moins un argument, la conversion implicite de Ex en Qx est un function_type_conversion, ou Mq est une méthode générique avec des paramètres de type {Y1, Y2, ..., Yq} et pour au moins un paramètre de type Yi l’argument de type est déduit d’un function_type, ou
  3. pour chaque argument, la conversion implicite de Ex en Qx n’est pas meilleure que la conversion implicite de Ex en Px, et pour au moins un argument, la conversion de Ex en Px est meilleure que la conversion de Ex en Qx.

Notez que l’ajout de cette règle ne va pas couvrir les scénarios où les méthodes d’instance deviennent pertinentes et masquent les méthodes d’extension. Par exemple:

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";
}

Le comportement de ce code passera silencieusement de l'impression de « string » à l'impression de « byte[] ».

Sommes-nous ok avec ce changement de comportement ? Doit-il être documenté comme une modification majeure ?

Notez qu’il n’existe aucune proposition pour rendre string_constant_to_UTF8_byte_representation_conversion indisponible lorsque la version linguistique C#10 est ciblée. Dans ce cas, l’exemple ci-dessus devient une erreur plutôt que de retourner au comportement C#10. Cela suit un principe général selon lequel la version du langage cible n’affecte pas la sémantique du langage.

Sommes-nous ok avec ce comportement ? Doit-il être documenté comme une modification majeure ?

La nouvelle règle n'empêchera pas non plus les ruptures impliquant des conversions de littéraux de tuple. Par exemple

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 imprimer silencieusement « array » au lieu de « object ».

Sommes-nous ok avec ce comportement ? Doit-on le documenter comme une rupture ? Peut-être pourrions-nous compliquer la nouvelle règle afin d'approfondir les conversions de littéraux de tuple.

Résolution :

Le prototype n'ajustera aucune règle ici, donc nous pouvons espérer voir ce qui est cassé dans la pratique - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.

(Résolu) Le suffixe u8 doit-il être insensible à la casse ?

Proposition :

Supporter le suffixe U8 également pour la cohérence avec les suffixes numériques.

Résolution :

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

Exemples aujourd’hui

Exemples d’emplacement où le runtime a encodé manuellement les octets UTF8 aujourd’hui

Exemples où nous laissons la perf sur la table

Concevoir des réunions

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