Sdílet prostřednictvím


Řetězcové literály UTF8

Poznámka

Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.

Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v příslušných poznámkách schůzky návrhu jazyka (LDM) .

Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .

Problém šampiona: https://github.com/dotnet/csharplang/issues/184

Shrnutí

Tento návrh přidává možnost psát řetězcové literály UTF8 v jazyce C# a nechat je automaticky zakódovat do jejich UTF-8 byte reprezentace.

Motivace

UTF8 je jazyk webu a jeho použití je nezbytné v významných částech zásobníku .NET. I když většina dat přichází ve formě byte[] ze síťového zásobníku, stále dochází k významnému využití konstant v kódu. Například síťová vrstva musí často zapisovat konstanty, jako "HTTP/1.0\r\n", " AUTH" nebo podobně. "Content-Length: ".

Dnes neexistuje žádná efektivní syntaxe, protože jazyk C# představuje všechny řetězce používající kódování UTF16. To znamená, že si vývojáři musí vybrat mezi pohodlím kódování za běhu, což přináší režijní náklady včetně času stráveného při spuštění prováděním operace kódování (a alokacemi, pokud je cíleno na typ, který je ve skutečnosti nevyžaduje), nebo ručním překladem bajtů a jejich uložením do 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 "));

Tento kompromis je obtížný bod, který se často objevuje pro naše partnery v runtime prostředí, ASP.NET a Azure. Často to vede k tomu, že nevyužijí svůj potenciál, protože se nechtějí zabývat ručním psaním kódování byte[].

Tento problém vyřešíme tak, že v jazyce povolíme literály UTF8 a zakódujeme je do byte[] UTF8 v době kompilace.

Podrobný návrh

u8 přípony u řetězcových literálů

Jazyk přidá příponu u8 řetězcovým literálům, aby byl typ UTF8. Přípona nerozlišuje velká a malá písmena, U8 přípona bude podporována a bude mít stejný význam jako přípona u8.

Při použití přípony u8 je hodnota literálu ReadOnlySpan<byte> obsahující bajtovou reprezentaci řetězce UTF-8. Ukončovací znak null se umístí za poslední bajt v paměti (a mimo délku ReadOnlySpan<byte>), aby bylo možné řešit některé scénáře interoperability, kde volání očekává řetězce zakončené hodnotou 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>'.

Vzhledem k tomu, že literály by byly přiděleny ve formě globálních konstant, životnost výsledného ReadOnlySpan<byte> by nezamezila jeho vrácení nebo předání k jinému použití. Některé kontexty, zejména v asynchronních funkcích, však neumožňují lokální proměnné typu ref struct, takže by v těchto situacích došlo k penalizaci využití a bude nutné volání funkce ToArray() nebo podobná akce.

Literál u8 nemá konstantní hodnotu. Je to proto, že ReadOnlySpan<byte> nemůže být typ konstanty dnes. Pokud je definice const rozšířena v budoucnu, aby zvážila ReadOnlySpan<byte>, měla by být tato hodnota také považována za konstantu. Prakticky to znamená, že literál u8 nelze použít jako výchozí hodnotu volitelného parametru.

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

Pokud je vstupní text pro literál poškozený řetězec ve formátu UTF16, jazyk bude hlásit chybu:

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.

Operátor sčítání

Nový odrážkový bod bude přidán do §12.10.5 Operátor sčítání následujícím způsobem.

  • Zřetězení bajtů UTF8:

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

    Tento binární + operátor provádí zřetězení bajtových sekvencí a je použitelný pouze v případě, že a pouze pokud jsou oba operandy sémanticky reprezentacemi UTF8 bajtů. Operand je sémanticky reprezentace bajtů UTF8, pokud se jedná o hodnotu u8 literálu, nebo hodnotu vytvořenou operátorem zřetězení bajtového vyjádření UTF8.

    Výsledkem zřetězení bajtové reprezentace UTF-8 je ReadOnlySpan<byte>, jež se skládá z bajtů levého operandu následovaných bajty pravého operandu. Ukončovací znak null se umístí za poslední bajt v paměti (a mimo délku ReadOnlySpan<byte>), aby bylo možné řešit některé scénáře interoperability, kde volání očekává řetězce zakončené hodnotou null.

Snížení

Jazyk sníží kódované řetězce UTF8 přesně tak, jako kdyby vývojář zadal výsledný literál byte[] do kódu. Například:

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.

To znamená, že všechny optimalizace, které platí pro formulář new byte[] { ... }, budou platit i pro literály utf8. To znamená, že volací místo bude bez alokace, protože jazyk C# to optimalizuje tak, aby byl uložen v části .data souboru PE.

Více po sobě jdoucích aplikací operátorů zřetězení bajtů UTF8 je sloučeno do jediného vytvoření objektu ReadOnlySpan<byte> s bajtovým polem obsahujícím konečnou posloupnost bajtů.

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.

Nevýhody

Spoléhání na základní rozhraní API

Implementace kompilátoru použije UTF8Encoding pro detekci neplatných řetězců i překlad do byte[]. Přesná rozhraní API budou pravděpodobně záviset na tom, jakou cílovou architekturu kompilátor používá. UTF8Encoding bude tahounem implementace.

Historicky se kompilátor vyhýbal použití runtime API pro zpracování literálů. Je to proto, že přebírá kontrolu nad tím, jak se konstanty zpracovávají mimo jazyk a do modulu runtime. Konkrétně to znamená, že položky, jako jsou opravy chyb, můžou měnit konstantní kódování a znamenat, že výsledek kompilace jazyka C# závisí na tom, na kterém modulu runtime kompilátor provádí.

Nejedná se o hypotetický problém. Dřívější verze Roslynu používaly double.Parse pro zpracování plovoucích desetinných konstant. To způsobilo řadu problémů. Nejprve to znamenalo, že některé hodnoty s plovoucí desetinnou čárkou měly různé reprezentace mezi nativním kompilátorem a Roslynem. Za druhé, jak se .NET Core vyvinul a opravil dlouhotrvající chyby v kódu double.Parse, znamenalo to, že význam těchto konstant se v jazyce změnil v závislosti na modulu runtime, na kterém byl kompilátor spuštěn. Výsledkem je, že kompilátor nakonec vytvořil vlastní verzi kódu pro analýzu plovoucích bodových čísel a odstranil závislost na double.Parse.

Tento scénář byl projednán s týmem runtime a necítíme, že má stejné problémy, na které jsme narazili dříve. Analýza UTF8 je stabilní napříč moduly runtime a v této oblasti neexistují žádné známé problémy, které by se měly týkat budoucích problémů s kompatibilitou. Pokud se objeví, můžeme strategii znovu vyhodnotit.

Alternativy

Pouze typ cíle

Návrh by mohl spoléhat pouze na cílový typ a odebrat příponu u8 u string literálů. Ve většině případů je string literál přímo přiřazen ReadOnlySpan<byte>, a proto není nutné.

ReadOnlySpan<byte> span = "Hello World;" 

Přípona u8 existuje hlavně pro podporu dvou scénářů: var a vyřešení přetížení. Pro druhou možnost zvažte následující případ použití:

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

Vzhledem k implementaci je lepší volat Write(ReadOnlySpan<byte>) a přípona u8 to usnadňuje: Write("hello"u8). Chybí-li, vývojáři se musí uchýlit k nevhodnému přetypování Write((ReadOnlySpan<byte>)"hello").

Tato položka je stále pohodlná, funkce může existovat bez ní a není zásadní přidat ji později.

Počkejte na typ Utf8String

I když ekosystém .NET standardizuje ReadOnlySpan<byte> jako de facto typ řetězce UTF8, je dnes možné, že modul runtime v budoucnosti zavede skutečný typ Utf8String.

Vyhodnocujeme náš návrh na základě této možné změny a reflektujeme, jestli bychom litovali rozhodnutí, která jsme udělali. I když bychom měli zvážit reálnou pravděpodobnost zavedení Utf8String, tato pravděpodobnost se každý den snižuje, jakmile najdeme ReadOnlySpan<byte> jako přijatelnou alternativu.

Zdá se nepravděpodobné, že bychom litovali převodu cílového typu mezi řetězcovými literály a ReadOnlySpan<byte>. Použití ReadOnlySpan<byte> jako utf8 je nyní vloženo do našich rozhraní API, a proto má převod stále hodnotu, i když přichází Utf8String a je to "lepší" typ. Jazyk může jednoduše preferovat převody na Utf8String než ReadOnlySpan<byte>.

Zdá se pravděpodobnější, že bychom litovali přípony u8 odkazující na ReadOnlySpan<byte> místo Utf8String. Bylo by podobné, jako bychom litovali, že stackalloc int[] má přirozený typ int* místo Span<int>. To ale není deal breaker, jen nepříjemnosti.

Převody mezi konstantami string a sekvencemi byte

Převody v této části nebyly implementovány. Tyto konverze zůstávají aktivními návrhy.

Jazyk umožní převody mezi string konstantami a sekvencemi byte, kde je text převeden na ekvivalentní reprezentaci bajtů UTF8. Konkrétně kompilátor umožní převod konstant řetězců na UTF8 bajtovou reprezentaci – implicitní převody z string konstant na byte[], Span<byte>a ReadOnlySpan<byte>. Do oddílu implicitních převodů §10.2 se přidá nový odrážkový bod. Tento převod není standardním převodem §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 }

Pokud je vstupní text pro převod poškozený řetězec UTF16, jazyk vygeneruje chybu:

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

Očekává se, že hlavní použití této funkce bude s literály, ale bude fungovat i s libovolnou konstantní hodnotou string. Bude podporován i převod z konstanty string s hodnotou null. Výsledkem převodu bude default hodnota cílového typu.

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

V případě jakékoli konstantní operace s řetězci, jako je například +, proběhne kódování do UTF8 až na koncovém string místo toho, aby se provádělo pro jednotlivé části a výsledky se pak zřetězily. Toto řazení je důležité vzít v úvahu, protože může mít vliv na to, jestli převod proběhne úspěšně nebo ne.

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

Tyto dvě části jsou samy o sobě neplatné, protože jsou neúplné části náhradní dvojice. Individuálně neexistuje žádný správný překlad do UTF8, ale společně tvoří kompletní náhradní pár, který lze úspěšně přeložit na UTF8.

V stromech výrazů Linq není povolená string_constant_to_UTF8_byte_representation_conversion.

I když vstupy těchto převodů jsou konstanty a data jsou plně kódována v době kompilace, převod je není považován za konstantu jazyka. Je to proto, že pole dnes nejsou konstantní. Pokud se v budoucnu definice const rozšíří tak, aby zohledňovala pole, měly by se také zvážit tyto převody. Prakticky i když to znamená, že výsledek těchto převodů nelze použít jako výchozí hodnotu volitelného parametru.

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

Po implementaci řetězcových literálů budou mít stejný problém jako ostatní literály v jazyce: jaký typ představují, závisí na tom, jak se používají. Jazyk C# poskytuje literálovou příponu k rozlišení významu ostatních literálů. Vývojáři mohou například napsat 3.14f, aby vynutili, že hodnota bude float, nebo 1l, aby hodnota byla long.

Nevyřešené otázky

První tři otázky ohledně návrhu se týkají převodů řetězce na Span<byte> / ReadOnlySpan<byte>. Nebyly dosud implementovány.

(Vyřešeno) Převody mezi konstantou string s null hodnotou a sekvencemi byte

Zda je tento převod podporován a, pokud ano, jak se provádí, není specifikováno.

Návrh:

Povolit implicitní převody z konstanty string s hodnotou null na byte[], Span<byte>a ReadOnlySpan<byte>. Výsledkem převodu je hodnota "default" cílového typu.

řešení :

Návrh je schválen - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.

(Vyřešeno) Kam string_constant_to_UTF8_byte_representation_conversion patří?

Je string_constant_to_UTF8_byte_representation_conversion samostatnou odrážkou v oddílu implicitních převodů §10.2, nebo je součástí §10.2.11, anebo patří do některé jiné existující skupiny implicitních převodů?

Návrh:

Jedná se o nový odrážkový bod v implicitních převodech §10.2, podobně jako "Implicitní interpolované převody řetězců" nebo "Převody skupin metod". Necítí se, že patří do "implicitních převodů konstantních výrazů", protože i když je zdrojem konstantní výraz, výsledek nikdy není konstantní výraz. Také "Implicitní převody konstantních výrazů" jsou považovány za "Standardní implicitní převody" §10.4.2, což je pravděpodobné, že vede ke změnám nestandardního chování zahrnujícího uživatelem definované převody.

řešení :

Zavedeme nový typ převodu pro řetězcovou konstantu na bajty UTF-8 – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds

(Vyřešeno) Je string_constant_to_UTF8_byte_representation_conversion standardní převod

Kromě "čistých" standardních převodů (standardní převody jsou ty předdefinované převody, které mohou nastat jako součást převodu definované uživatelem), kompilátor také považuje některé předdefinované převody za "poněkud" standard. Například implicitní interpolovaný převod řetězců může nastat jako součást uživatelem definovaného převodu, pokud existuje explicitní přetypování na cílový typ v kódu. Jako by se jedná o standardní explicitní převod, i když se jedná o implicitní převod, který není explicitně zahrnut do sady standardních implicitních nebo explicitních převodů. Například:

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

Návrh:

Nový převod není standardním převodem. Tím se zabrání jiným než triviálním změnám chování zahrnujícím uživatelem definované převody. Nebudeme se například muset starat o uživatelem definované konverze při implicitních konverzích literálů n-tic atd.

řešení :

Není to standardní převod, zatím – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.

(Vyřešeno) Převod výrazového stromu LINQ

Mělo by být string_constant_to_UTF8_byte_representation_conversion povoleno v kontextu převodu v rámci Linq Expression Tree? Prozatím ho můžeme odmítnout, nebo můžeme jednoduše zahrnout "sníženou podobu" do stromu. Například:

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

A co řetězcové literály se sufixem u8? Mohli bychom je vypsat jako vytváření bajtových polí:

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

řešení :

Nepovoleno ve stromech výrazů LINQ - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.

(Vyřešeno) Přirozený typ řetězcového literálu s příponou u8

Část "Podrobný návrh" uvádí: "Přirozeným typem však bude ReadOnlySpan<byte>." Zároveň: "Když je použita přípona u8, může být literál stále převeden na některý z povolených typů: byte[], Span<byte> nebo ReadOnlySpan<byte>."

Tento přístup má několik nevýhod:

  • ReadOnlySpan<byte> není k dispozici v desktopové rozhraní;
  • Neexistují žádné převody z ReadOnlySpan<byte> na byte[] nebo Span<byte>. Abychom je mohli podporovat, budeme pravděpodobně muset zacházet s literály tak, že je uzpůsobíme pro cílový typ. Pravidla jazyka i implementace budou složitější.

Návrh:

Přirozený typ bude byte[]. Je snadno dostupný ve všech architekturách. Mimochodem, za běhu budeme vždy začínat vytvořením bajtového pole, a to i s původním návrhem. K podpoře převodů na Span<byte> a ReadOnlySpan<byte>také nepotřebujeme žádná zvláštní pravidla převodu . Již existují implicitní uživatelem definované převody z byte[] na Span<byte> a ReadOnlySpan<byte>. Existuje dokonce implicitní převod definovaný uživatelem na ReadOnlyMemory<byte> (viz níže uvedená otázka Hloubka převodu). Existuje nevýhoda, jazyk neumožňuje řetězení uživatelsky definovaných převodů. Následující kód se tedy nezkompiluje:

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

Stejně jako u jakékoli uživatelem definované konverze lze použít explicitní přetypování, aby se jedna uživatelem definovaná konverze stala součástí jiné.

Zdá se, že všechny motivační scénáře budou řešeny s použitím byte[] jako přirozeného typu, ale díky tomu budou pravidla jazyka a implementace výrazně jednodušší.

řešení :

Návrh je schválen - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals. Pravděpodobně budeme chtít mít hlubší debatu o tom, zda by u8 řetězcové literály měly mít typ proměnného pole, ale nemyslíme si, že je tato debata zatím nezbytná.

Byl implementován pouze explicitní operátor převodu.

(Vyřešeno) Hloubka převodu

Bude fungovat i kdekoli, kde by mohl fungovat bajt[]? Uvažujte:

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

První příklad by pravděpodobně měl fungovat z důvodu přirozeného typu, který pochází z u8.

Druhý příklad je obtížné zprovoznit, protože vyžaduje převody v obou směrech. To ovšem neplatí, pokud přidáme ReadOnlyMemory<byte> jako jeden z povolených typů převodu.

Návrh:

Nedělejte nic zvláštního.

řešení :

Nyní nejsou přidány žádné nové cíle převodu https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Ani jeden z převodů se nekompiluje.

(Vyřešeno) Chyba při rozlišení přetížení

Následující rozhraní API by bylo nejednoznačné:

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

Co bychom měli udělat, abychom to vyřešili?

Návrh:

Podobně jako https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolutionje člen lepší funkce (§11.6.4.3) aktualizován tak, aby upřednostňoval členy, u kterých žádný z převodů nevyžaduje převod string konstant na sekvence UTF8 byte.

Lepší člen funkce

... Při A seznamu argumentů se sadou výrazů argumentů {E1, E2, ..., En} a dvěma platnými členy funkce Mp a Mq s typy parametrů {P1, P2, ..., Pn} a {Q1, Q2, ..., Qn}, Mp je definován jako lepší člen funkce než Mq pokud

  1. pro každý argument tam není implicitní převod z Ex na Pxstring_constant_to_UTF8_byte_representation_conversiona pro alespoň jeden argument implicitní převod z Ex na Qx je string_constant_to_UTF8_byte_representation_conversionnebo
  2. Pro každý argument není implicitní převod z Ex na Px funkcí typu konverze a
    • Mp je negenerická metoda nebo Mp je generická metoda s parametry typu {X1, X2, ..., Xp} a pro každý parametr typu Xi je argument typu odvozen z výrazu nebo jiného typu než function_typea
    • alespoň pro jeden argument je implicitní převod z Ex na Qxfunction_type_conversion, nebo Mq je obecná metoda s parametry typu {Y1, Y2, ..., Yq} a alespoň pro jeden parametr typu Yi je argument typu odvozen z function_type, nebo
  3. pro každý argument implicitní převod z Ex na Qx není lepší než implicitní převod z Ex na Pxa pro alespoň jeden argument je převod z Ex na Px lepší než převod z Ex na Qx.

Všimněte si, že přidání tohoto pravidla nepokrývá scénáře, kdy se instance metody stanou použitelnými a překrývají rozšiřující metody. Například:

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

Chování tohoto kódu se tiše změní z vypisování "string" na vypisování "byte[]".

Jsme v pořádku s touto změnou chování? Je třeba ji zdokumentovat jako zásadní změnu?

Všimněte si, že neexistuje návrh učinit string_constant_to_UTF8_byte_representation_conversion nedostupným při použití jazykové verze C#10 jako cíle. V takovém případě se výše uvedený příklad stane chybou místo toho, aby se vrátil do chování jazyka C#10. To se řídí obecným principem, že cílová verze jazyka nijak neovlivňuje sémantiku daného jazyka.

Jsme s tímto chováním v pořádku? Je třeba ji zdokumentovat jako zásadní změnu?

Nové pravidlo také nezabrání přerušením, které zahrnují převody literálů n-tic. Například

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

bude bezobslužně tisknout pole místo objektu.

Jsme s tímto chováním v pořádku? Je třeba ji zdokumentovat jako zásadní změnu? Možná bychom mohli nové pravidlo více rozvinout, abychom prozkoumali konverze literálů řazených n-tic.

řešení :

Prototyp zde neupraví žádná pravidla, takže doufáme, že uvidíme, co se v praxi pokazí - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.

(Vyřešeno) Měla by přípona u8 nerozlišovat malá a velká písmena?

Návrh:

Podpora přípony U8 i pro konzistenci s číselnými příponami.

řešení :

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

Příklady dnes

Příklady toho, kde modul runtime dnes ručně kódoval bajty UTF8

Příklady, kdy ponecháme výkon v tabulce

Projekční schůzky

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