Delen via


Utf8-tekenreeksen letterlijke tekens

Notitie

Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.

Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de betreffende notities van de LDM (Language Design Meeting).

Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.

Kampioenprobleem: https://github.com/dotnet/csharplang/issues/184

Samenvatting

Dit voorstel voegt de mogelijkheid toe om letterlijke tekenreeksen van UTF8 in C# te schrijven en deze automatisch te laten coderen in hun UTF-8 byte weergave.

Motivatie

UTF8 is de taal van het web en het gebruik ervan is noodzakelijk in belangrijke delen van de .NET-stack. Hoewel veel gegevens in de vorm van byte[] van de netwerkstack komen, zijn er nog steeds aanzienlijke toepassingen van constanten in de code. Netwerkstack moet bijvoorbeeld vaak constanten schrijven, zoals "HTTP/1.0\r\n", " AUTH" of . "Content-Length: ".

Tegenwoordig is er geen efficiënte syntaxis om dit te doen, omdat C# alle tekenreeksen vertegenwoordigt met behulp van UTF16-codering. Dit betekent dat ontwikkelaars moeten kiezen tussen het gemak van codering tijdens runtime, waardoor overhead in rekening wordt gebracht, inclusief de tijd die tijdens het opstarten daadwerkelijk wordt besteed aan het uitvoeren van de coderingsbewerking (en toewijzingen als het gaat om een type waarvoor ze niet daadwerkelijk nodig zijn), of het handmatig vertalen van de bytes en het opslaan in een 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 "));

Deze afweging is een pijnpunt dat vaak opduikt voor onze partners in de runtime, ASP.NET en Azure. Vaak zorgt het ervoor dat ze niet maximaal presteren, omdat ze geen zin hebben in het gedoe van het handmatig uitschrijven van de byte[]-codering.

U kunt dit oplossen door letterlijke waarden van UTF8 in de taal toe te staan en deze te coderen in de UTF8-byte[] tijdens het compileren.

Gedetailleerd ontwerp

u8 achtervoegsel op letterlijke tekenreeksen

De taal geeft het u8 achtervoegsel op letterlijke tekenreeksen om af te dwingen dat het type UTF8 is. Het achtervoegsel is niet hoofdlettergevoelig, U8 achtervoegsel wordt ondersteund en heeft dezelfde betekenis als u8 achtervoegsel.

Wanneer het achtervoegsel u8 wordt gebruikt, is de waarde van de letterlijke waarde een ReadOnlySpan<byte> met een UTF-8-byteweergave van de tekenreeks. Een null-eindteken wordt buiten de laatste byte in het geheugen geplaatst (en buiten de lengte van de ReadOnlySpan<byte>) om enkele interop-scenario's af te handelen waarbij de aanroep null-beëindigde tekenreeksen verwacht.

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>'.

Omdat de letterlijke gegevens als globale constanten zouden worden toegewezen, zou de levensduur van de resulterende ReadOnlySpan<byte> niet verhinderen dat deze ergens anders wordt geretourneerd of doorgegeven. Bepaalde contexten, met name binnen asynchrone functies, staan echter geen lokale variabelen van ref struct-typen toe, dus zouden er in die situaties nadelen zijn, waarbij een ToArray()-aanroep of iets vergelijkbaars vereist is.

Een u8 literal heeft geen constante waarde. Dat komt doordat ReadOnlySpan<byte> vandaag niet het type van een constante kan zijn. Als de definitie van const in de toekomst wordt uitgebreid om rekening te houden met ReadOnlySpan<byte>, moet deze waarde ook worden beschouwd als een constante. Praktisch gezien betekent dit dat een u8 letterlijke waarde niet kan worden gebruikt als de standaardwaarde van een optionele parameter.

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

Wanneer de invoertekst voor de literal een ongeldige UTF16-tekenreeks is, zal de taal een fout genereren:

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.

Operator voor optellen

Er wordt als volgt een nieuw opsommingsteken toegevoegd aan §12.10.5 Toevoegingsoperator.

  • Samenvoeging van UTF8-byteweergave:

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

    Deze binaire + operator voert bytereeksen samenvoeging uit en is alleen van toepassing als beide operanden semantisch UTF8-byteweergaven zijn. Een operand is semantisch een UTF8-byteweergave wanneer het een waarde is van een u8 letterlijke waarde of een waarde die wordt geproduceerd door de UTF8-bytesamenvoegingsoperator.

    Het resultaat van de samenvoeging van de UTF8-byteweergave is een ReadOnlySpan<byte> die bestaat uit de bytes van de linkeroperand gevolgd door de bytes van de rechteroperand. Een null-eindteken wordt buiten de laatste byte in het geheugen geplaatst (en buiten de lengte van de ReadOnlySpan<byte>) om enkele interop-scenario's af te handelen waarbij de aanroep null-beëindigde tekenreeksen verwacht.

Verlaging

De taal verlaagt de gecodeerde UTF8-tekenreeksen precies alsof de ontwikkelaar de resulterende byte[] letterlijke code had getypt. Bijvoorbeeld:

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.

Dat betekent dat alle optimalisaties die van toepassing zijn op het new byte[] { ... } formulier ook van toepassing zijn op letterlijke utf8-gegevens. Dit betekent dat het aanroepingspunt zonder extra geheugenallocatie is, aangezien C# dit optimaliseert om in de .data-sectie van het PE-bestand op te slaan.

Meerdere opeenvolgende toepassingen van samenvoegingsoperators voor UTF8-byteweergave worden samengevouwen tot één creatie van ReadOnlySpan<byte> met bytematrix die de uiteindelijke bytereeks bevat.

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.

Nadelen

Afhankelijk van kern-API's

De compiler-implementatie gebruikt UTF8Encoding voor zowel ongeldige tekenreeksdetectie als vertaling naar byte[]. De exacte API's zijn mogelijk afhankelijk van welk doelframework de compiler gebruikt. Maar UTF8Encoding zal de krachtpatser van de implementatie zijn.

In het verleden heeft de compiler het gebruik van runtime-API's voor letterlijke verwerking vermeden. Dat komt doordat er controle wordt verkregen over de wijze waarop constanten worden verwerkt uit de taal en in de runtime. Concreet betekent dit dat items zoals bugfixes constante codering kunnen wijzigen en dat het resultaat van C#-compilatie afhankelijk is van de runtime waarop de compiler wordt uitgevoerd.

Dit is geen hypothetisch probleem. Vroege versies van Roslyn gebruikten double.Parse om drijvendekommaconstante parsering te verwerken. Dat heeft een aantal problemen veroorzaakt. Eerst betekende het dat sommige drijvendekommawaarden verschillende representaties hadden tussen de systeemeigen compiler en Roslyn. Ten tweede, omdat .NET Core lang bestaande bugs in de double.Parse code heeft ontwikkeld en opgelost, betekende dit dat de betekenis van deze constanten in de taal werd gewijzigd, afhankelijk van de runtime waarop de compiler werd uitgevoerd. Hierdoor moest de compiler zijn eigen versie van de drijvende-komma-parsercode schrijven en de afhankelijkheid van double.Parseverwijderen.

Dit scenario is besproken met het runtime-team en we voelen niet dat het dezelfde problemen heeft waar we eerder tegenaan zijn gelopen. De UTF8-parsing is stabiel bij verschillende runtimes en er zijn geen bekende problemen die voor toekomstige compatibiliteitsproblemen kunnen zorgen. Als er een komt, kunnen we de strategie opnieuw evalueren.

Alternatieven

Alleen doeltype

Het ontwerp kan uitsluitend afhankelijk zijn van doeltypen en het achtervoegsel u8 bij string-literalen verwijderen. In de meeste gevallen wordt de letterlijke string rechtstreeks toegewezen aan een ReadOnlySpan<byte> daarom is het niet nodig.

ReadOnlySpan<byte> span = "Hello World;" 

Het achtervoegsel u8 bestaat voornamelijk ter ondersteuning van twee scenario's: var en overbelastingsresolutie. Voor deze laatste moet u rekening houden met de volgende use-case:

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

Gezien de implementatie is het beter om Write(ReadOnlySpan<byte>) aan te roepen en het u8 achtervoegsel maakt dit handig: Write("hello"u8). Ontwikkelaars moeten noodgedwongen hun toevlucht nemen tot onhandig casten Write((ReadOnlySpan<byte>)"hello").

Toch is dit een handig item, de functie kan zonder deze bestaan en het is niet belangrijk om het op een later tijdstip toe te voegen.

Wacht op het Utf8String-type

Hoewel het .NET-ecosysteem vandaag de dag standaardiseert op ReadOnlySpan<byte> als het defacto Utf8-tekenreeks type, is het mogelijk dat de runtime in de toekomst een werkelijk Utf8String-type introduceert.

We moeten ons ontwerp hier evalueren ten aanzien van deze mogelijke wijziging en nadenken over of we spijt hebben van de beslissingen die we hebben genomen. Dit moet echter worden afgewogen tegen de realistische kans dat we Utf8Stringzullen introduceren, een kans die elke dag lijkt te verminderen naarmate we ReadOnlySpan<byte> als een acceptabel alternatief vinden.

Het lijkt onwaarschijnlijk dat we spijt hebben van de conversie van het doeltype tussen letterlijke tekenreeksen en ReadOnlySpan<byte>. Het gebruik van ReadOnlySpan<byte> als utf8 is nu ingesloten in onze API's en daarom is er nog steeds waarde in de conversie, zelfs als Utf8String langskomt en een 'beter' type is. De taal kan gewoon de voorkeur geven aan conversies naar Utf8String boven ReadOnlySpan<byte>.

Het lijkt waarschijnlijker dat we het u8 achtervoegsel betreuren dat verwijst naar ReadOnlySpan<byte> in plaats van Utf8String. Het zou vergelijkbaar zijn met hoe we betreuren dat stackalloc int[] een natuurlijk type int* heeft in plaats van Span<int>. Dit is maar geen afknapper, slechts een ongemak.

Conversies tussen string constanten en byte reeksen

De conversies in deze sectie zijn niet geïmplementeerd. Deze conversies blijven actieve voorstellen.

De taal biedt conversies tussen string constanten en byte reeksen waarin de tekst wordt geconverteerd naar de equivalente UTF8-byteweergave. Met name staat de compiler string_constant_to_UTF8_byte_representation_conversion impliciete conversies van string constanten naar byte[], Span<byte>en ReadOnlySpan<byte>toe. Er wordt een nieuw opsommingsteken toegevoegd aan de sectie over impliciete conversies §10.2 . Deze conversie is geen standaardconversie §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 }

Wanneer de invoertekst voor de conversie een ongeldige UTF-16-tekenreeks is, zullen er foutmeldingen worden gegenereerd.

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

Het overheersende gebruik van deze functie is naar verwachting met letterlijke waarden, maar het werkt met elke string constante waarde. Een conversie van een string constante met null waarde wordt ook ondersteund. Het resultaat van de conversie wordt default waarde van het doeltype.

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

In het geval van een constante bewerking op tekenreeksen, zoals +, zal de codering in UTF8-formaat plaatsvinden op de laatste string, in plaats van dat het voor de afzonderlijke delen gebeurt en de resultaten worden samengevoegd. Deze volgorde is belangrijk om rekening mee te houden, omdat dit van invloed kan zijn op of de conversie wel of niet slaagt.

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

De twee delen hier zijn op zichzelf ongeldig omdat ze onvolledige delen van een surrogaatpaar zijn. Afzonderlijk is er geen juiste vertaling naar UTF8, maar samen vormen ze een volledig surrogaatpaar dat kan worden vertaald naar UTF8.

In Linq Expression Trees is de string_constant_to_UTF8_byte_representation_conversion niet toegestaan.

Hoewel de invoer voor deze conversies constanten zijn en de gegevens tijdens het compileren volledig zijn gecodeerd, wordt de conversie niet beschouwd als constante door de taal. Dat komt doordat matrices vandaag niet constant zijn. Als de definitie van const in de toekomst wordt uitgebreid om matrices te overwegen, moeten deze conversies ook worden overwogen. Praktisch gezien betekent dit dat een resultaat van deze conversies niet kan worden gebruikt als de standaardwaarde van een optionele parameter.

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

Zodra tekenreeksconstanten zijn geïmplementeerd, hebben ze hetzelfde probleem als andere constanten in de taal: welk type ze vertegenwoordigen, is afhankelijk van hoe ze worden gebruikt. C# biedt een literale achtervoegsel om de betekenis voor andere literalen te verduidelijken. Ontwikkelaars kunnen bijvoorbeeld 3.14f schrijven om af te dwingen dat de waarde een float is of 1l om de waarde af te dwingen een longte zijn.

Niet-opgeloste vragen

De eerste drie ontwerpvragen hebben betrekking op conversies van tekenreeksen naar Span<byte> / ReadOnlySpan<byte>. Deze zijn nog niet geïmplementeerd.

(Opgelost) Conversies tussen een string constante met null waarde en byte reeksen

Of deze conversie wordt ondersteund en zo ja, hoe deze wordt uitgevoerd, wordt niet opgegeven.

Voorstel:

Impliciete conversies van een string constante met null waarde toestaan naar byte[], Span<byte>en ReadOnlySpan<byte>. Het resultaat van de conversie is default waarde van het doeltype.

oplossing:

Het voorstel wordt goedgekeurd - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.

(Opgelost) Waar hoort string_constant_to_UTF8_byte_representation_conversion bij?

Is string_constant_to_UTF8_byte_representation_conversion een opsommingsteken in de impliciete conversies sectie §10.2 zelfstandig, of maakt het deel uit van §10.2.11, of behoort het tot een andere bestaande impliciete conversiegroep?

Voorstel:

Het is een nieuw opsommingsteken in impliciete conversies §10.2, vergelijkbaar met impliciete geïnterpoleerde tekenreeksconversies of methodegroepconversies. Het lijkt erop dat het geen deel uitmaakt van impliciete expressieconversies, omdat, zelfs als de bron een constante expressie is, het resultaat nooit een constante expressie is. Ook 'Impliciete constante expressieconversies' worden beschouwd als 'Standaard impliciete conversies' §10.4.2, wat waarschijnlijk leidt tot niet-triviale gedragswijzigingen met door de gebruiker gedefinieerde conversies.

oplossing:

We introduceren een nieuw type conversie voor tekenreeksconstante naar UTF-8 bytes - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds

(Opgelost) Is string_constant_to_UTF8_byte_representation_conversion een standaard conversie?

Naast 'pure' standaardconversies (de standaardconversies zijn deze vooraf gedefinieerde conversies die kunnen optreden als onderdeel van een door de gebruiker gedefinieerde conversie), behandelt compiler ook enkele vooraf gedefinieerde conversies als 'enigszins' standaard. Een impliciete geïnterpoleerde tekenreeksconversie kan bijvoorbeeld optreden als onderdeel van een door de gebruiker gedefinieerde conversie als er een expliciete cast is naar het doeltype in code. Alsof het een standaard expliciete conversie is, ook al is het een impliciete conversie die niet expliciet is opgenomen in de set standaard impliciete of expliciete conversies. Bijvoorbeeld:

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

Voorstel:

De nieuwe conversie is geen standaardconversie. Dit voorkomt niet-triviale gedragswijzigingen met door de gebruiker gedefinieerde conversies. We hoeven ons bijvoorbeeld geen zorgen te maken over door de gebruiker gedefinieerde cinversions onder impliciete tuple-letterlijke conversies, enzovoort.

oplossing:

Geen standaardconversie, voorlopig - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.

(Opgelost) Conversie van LINQ-expressieboom

Moet string_constant_to_UTF8_byte_representation_conversion worden toegestaan in de context van een conversie van de Linq-expressieboom? We kunnen het voorlopig niet toegestaan hebben, of we kunnen gewoon de 'verlaagde' vorm in de boom opnemen. Bijvoorbeeld:

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

Hoe zit het met tekenreeksen met de achtervoegsel u8? We kunnen deze zichtbaar maken als byte-arraycreaties.

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

oplossing:

Verbieden in LINQ-Expressiebomen - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.

(Opgelost) Het natuurlijke type van een letterlijke tekenreeks met het u8-achtervoegsel

De sectie 'Gedetailleerd ontwerp' zegt: 'Het natuurlijke type wordt echter ReadOnlySpan<byte>'. Tegelijkertijd: "Wanneer het u8 achtervoegsel wordt gebruikt, kan de letterlijke tekst nog steeds worden geconverteerd naar een van de toegestane typen: byte[], Span<byte> of ReadOnlySpan<byte>."

Er zijn verschillende nadelen met deze benadering:

  • ReadOnlySpan<byte> is in het desktopframework niet beschikbaar;
  • Er zijn geen bestaande conversies van ReadOnlySpan<byte> naar byte[] of Span<byte>. Om ze te ondersteunen, moeten we de letterlijke gegevens waarschijnlijk behandelen als doeltype. Zowel de taalregels als de implementatie worden ingewikkelder.

Voorstel:

Het natuurlijke type wordt byte[]. Deze is direct beschikbaar voor alle frameworks. BTW, tijdens runtime beginnen we altijd met het maken van een bytematrix, zelfs met het oorspronkelijke voorstel. We hebben ook geen speciale conversieregels nodig om conversies naar Span<byte> en ReadOnlySpan<byte>te ondersteunen. Er zijn al impliciete door de gebruiker gedefinieerde conversies van byte[] naar Span<byte> en ReadOnlySpan<byte>. Er is zelfs impliciete door de gebruiker gedefinieerde conversie naar ReadOnlyMemory<byte> (zie de vraag 'Diepte van de conversie' hieronder). Er is een nadeel, taal staat het koppelen van door de gebruiker gedefinieerde conversies niet toe. De volgende code wordt dus niet gecompileerd:

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

Net als bij elke door de gebruiker gedefinieerde conversie kan echter een expliciete cast worden gebruikt om een door de gebruiker gedefinieerde conversie een deel van een andere door de gebruiker gedefinieerde conversie te maken.

Het lijkt erop dat alle motiverende scenario's worden aangepakt met byte[] als het natuurlijke type, maar de taalregels en implementatie zullen aanzienlijk eenvoudiger zijn.

oplossing:

Het voorstel wordt goedgekeurd - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals. We zullen waarschijnlijk een dieper debat willen voeren over de vraag of u8 string-literals een type veranderlijke array moeten hebben, maar we denken niet dat dat debat op dit moment nodig is.

Alleen de expliciete conversieoperator is geïmplementeerd.

(Opgelost) Diepte van de conversie

Werkt het ook overal waar een byte[] zou kunnen werken? Overwegen:

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

Het eerste voorbeeld zou waarschijnlijk moeten werken vanwege het natuurlijke type dat afkomstig is van u8.

Het tweede voorbeeld is moeilijk te realiseren omdat er conversies in beide richtingen nodig zijn. Dat is tenzij we ReadOnlyMemory<byte> toevoegen als een van de toegestane conversietypen.

Voorstel:

Doe niets speciaals.

oplossing:

Er zijn momenteel geen nieuwe conversiedoelen toegevoegd https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Geen van beide conversies compileert.

(Opgelost) Overbelastingsresolutie-einden

De volgende API wordt dubbelzinnig:

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

Wat moeten we doen om dit aan te pakken?

Voorstel:

Vergelijkbaar met https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, wordt het functielid Better function (§11.6.4.3) bijgewerkt om de voorkeur te geven aan functieleden waarbij geen van de betrokken conversies vereist dat string-constanten worden omgezet naar UTF8-byte-reeksen.

Beter functielid

... Gezien een lijst met argumenten A met een set argumentexpressies {E1, E2, ..., En} en twee toepasselijke functieleden Mp en Mq met parametertypen {P1, P2, ..., Pn} en {Q1, Q2, ..., Qn}, wordt Mp gedefinieerd als een beter functielid dan Mq als

  1. Voor elk argument is de impliciete conversie van Ex naar Px geen string_constante_naar_UTF8_byte_representatie_conversie, en voor ten minste één argument is de impliciete conversie van Ex naar Qx een string_constante_naar_UTF8_byte_representatie_conversieof
  2. Voor elk argument is de impliciete conversie van Ex naar Px geen function_type_conversion, en
    • Mp een niet-algemene methode of Mp een algemene methode is met typeparameters {X1, X2, ..., Xp} en voor elke typeparameter Xi het typeargument wordt afgeleid van een expressie of van een ander type dan een function_type, en
    • voor ten minste één argument is de impliciete conversie van Ex naar Qx een function_type_conversionof Mq een algemene methode met typeparameters {Y1, Y2, ..., Yq} en voor ten minste één typeparameter Yi het typeargument wordt afgeleid uit een function_typeof
  3. voor elk argument is de impliciete conversie van Ex naar Qx niet beter dan de impliciete conversie van Ex naar Px, en voor ten minste één argument is de conversie van Ex naar Px beter dan de conversie van Ex naar Qx.

Houd er rekening mee dat de toevoeging van deze regel niet betrekking heeft op scenario's waarbij exemplaarmethoden van toepassing worden en uitbreidingsmethoden voor schaduwen. Bijvoorbeeld:

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

Het gedrag van deze code verandert onopgemerkt van het weergeven van 'tekenreeks' naar het weergeven van 'byte[]'.

Zijn we in orde met deze gedragswijziging? Moet deze worden gedocumenteerd als een belangrijke wijziging?

Houd er rekening mee dat er geen voorstel is om string_constant_to_UTF8_byte_representation_conversion niet beschikbaar te maken wanneer de C#10-taalversie wordt gebruikt. In dat geval wordt het bovenstaande voorbeeld een fout in plaats van terug te keren naar C#10-gedrag. Dit volgt een algemeen principe dat de doeltaalversie geen invloed heeft op de semantiek van de taal.

Zijn we in orde met dit gedrag? Moet deze worden gedocumenteerd als een belangrijke wijziging?

De nieuwe regel voorkomt ook geen onderbrekingen met letterlijke tuple-conversies. Bijvoorbeeld

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

gaat stilletjes 'array' afdrukken in plaats van 'object'.

Zijn we in orde met dit gedrag? Moet deze worden gedocumenteerd als een belangrijke wijziging? Misschien kunnen we de nieuwe regel bemoeilijken om de letterlijke tuple-conversies te onderzoeken.

oplossing:

Het prototype zal hier geen regels aanpassen, zodat we hopelijk kunnen zien wat er in de praktijk kapot gaat - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.

(Opgelost) Moet u8 achtervoegsel hoofdletterongevoelig zijn?

Voorstel:

Ondersteun ook het U8-achtervoegsel voor consistentie met numerieke achtervoegsels.

oplossing:

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

Voorbeelden vandaag

Voorbeelden van waar runtime de UTF8-bytes vandaag handmatig heeft gecodeerd

Voorbeelden waarbij we perf op tafel laten staan

Ontwerpvergaderingen

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