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 eenu8
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 deReadOnlySpan<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.Parse
verwijderen.
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 Utf8String
zullen 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 long
te 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>
naarbyte[]
ofSpan<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 functieledenMp
enMq
met parametertypen{P1, P2, ..., Pn}
en{Q1, Q2, ..., Qn}
, wordtMp
gedefinieerd als een beter functielid danMq
als
- Voor elk argument is de impliciete conversie van
Ex
naarPx
geen string_constante_naar_UTF8_byte_representatie_conversie, en voor ten minste één argument is de impliciete conversie vanEx
naarQx
een string_constante_naar_UTF8_byte_representatie_conversieof- Voor elk argument is de impliciete conversie van
Ex
naarPx
geen function_type_conversion, en
Mp
een niet-algemene methode ofMp
een algemene methode is met typeparameters{X1, X2, ..., Xp}
en voor elke typeparameterXi
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
naarQx
een function_type_conversionofMq
een algemene methode met typeparameters{Y1, Y2, ..., Yq}
en voor ten minste één typeparameterYi
het typeargument wordt afgeleid uit een function_typeof- voor elk argument is de impliciete conversie van
Ex
naarQx
niet beter dan de impliciete conversie vanEx
naarPx
, en voor ten minste één argument is de conversie vanEx
naarPx
beter dan de conversie vanEx
naarQx
.
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
- 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
Voorbeelden waarbij we perf op tafel laten staan
- 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
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
C# feature specifications