Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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 ze schůzky jazykového návrhu (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/8887
Motivation
Funkce výrazu slovníku identifikovala potřebu předání výrazů kolekce s daty zadanými uživatelem, aby bylo možné nakonfigurovat chování konečné kolekce. Slovníky konkrétně umožňují uživatelům přizpůsobit způsob porovnávání klíčů pomocí nich k definování rovnosti mezi klíči a řazením nebo hashováním (v případě seřazených nebo hashovaných kolekcí). Tato potřeba se vztahuje při vytváření libovolného typu slovníku (například D d = new D(...)D d = D.CreateRange(...) , a dokonceIDictionary<...> d = <synthesized dict>)
Pro podporu tohoto problému se nový with(...arguments...) prvek navrhuje jako první prvek výrazu kolekce, například takto:
Dictionary<string, int> nameToAge = [with(comparer), .. d1, .. d2, .. d3];
- Při převodu
new CollectionType(...)na volání se...arguments...používají k určení příslušného konstruktoru a odpovídajícím způsobem se předávají. - Při převodu
CollectionFactory.Createna volání se předávají...arguments...předReadOnlySpan<ElementType>argumenty prvků, z nichž všechny slouží k určení příslušnéhoCreatepřetížení a jsou předány odpovídajícím způsobem. - Při převodu na rozhraní (například
IDictionary<,>) je povolen pouze jeden argument. Implementuje jedno z dobře známých rozhraní porovnávače BCL a použije se k řízení sémantiky porovnávání klíčů konečné instance.
Tato syntaxe byla zvolena takto:
- Uchovává všechny informace v syntaxi
[...]. Zajištění, že kód stále jasně indikuje vytvářenou kolekci. - Neznamená volání konstruktoru
new(pokud to není způsob vytvoření všech kolekcí). - Neznamená to, že se hodnoty kolekce vytvářejí nebo kopírují vícekrát (například příponu
with { ... }může. - Neodpovídá pořadí operací, zejména u sémantiky vyhodnocování pořadí řazení výrazů zleva doprava v jazyce C#. Například nevyhodnocuje argumenty použité k vytvoření kolekce po vyhodnocení výrazů použitých k naplnění kolekce.
- Nevynucuje uživateli čtení na konec (potenciálně velkého) výrazu kolekce k určení základní sémantiky chování. Například když se musí zobrazit na konec stořádkového slovníku, jenom aby se zjistilo, že ano, používal správný porovnávač klíčů.
- Není ani jemné, ale také příliš podrobné. Například použití
;namísto,označení argumentů je velmi snadná část syntaxe, která se má vynechat.with()přidá pouze 6 znaků a snadno vynikne, zejména při barvení syntaxe klíčovéhowithslova. - Pěkně čte. "Toto je výraz kolekce "with" tyto argumenty, který se skládá z těchto prvků."
- Řeší potřebu porovnávačů pro slovníky i sady.
- Zajišťuje, že každý uživatel potřebuje předávat argumenty nebo jakékoli potřeby, které máme nad rámec porovnávačů v budoucnu, se už zpracovávají.
- Není v konfliktu s žádným existujícím kódem (pomocí https://grep.app/ vyhledávání).
Filozofie návrhu
Následující část popisuje předchozí diskuze o filozofie návrhu. Včetně toho, proč byly některé formuláře odmítnuty.
Existují dva hlavní směry, kterým můžeme zadat tato uživatelsky definovaná data. Prvním je speciální případ pouze hodnoty v prostoru porovnávače (které definujeme jako typy dědící z seznamu BCL IComparer<T> nebo IEqualityComparer<T> typů). Druhým je poskytnutí generalizovaného mechanismu pro zadání libovolných argumentů do konečného vyvolaného rozhraní API při vytváření výrazů kolekce. Specifikace výrazu primárního slovníku ukazuje, jak bychom mohli provést první výraz, zatímco tato specifikace se snaží provést tu druhou.
Pokud bychom chtěli rozšířit řešení na libovolné argumenty, ukázaly se v jejich přístupu kontroly řešení jen pro úspěšné porovnání slabiny. Například:
Opakovaně se používá syntaxe elementů , stejně jako u formuláře:
[StringComparer.OrdinalIgnoreCase, "mads": 21]. To funguje dobře v prostoru, kdeKeyValuePair<,>a porovnávače nedědí z běžných typů. Ale rozpadá se ve světě, kde by mohl udělat:HashSet<object> h = [StringComparer.OrdinalIgnoreCase, "v"]. Předává se to porovnávacímu? Nebo se pokoušíte do sady vložit dvě hodnoty objektu?Oddělení argumentů a prvků pomocí jemné syntaxe (například použití středníku místo čárky k jejich oddělení
[comparer; v1]). To znamená velmi matoucí situace, kdy uživatel omylem zapíše[1; 2](a získá kolekci, která předává "1" jako například argument "kapacita" pro aList<>obsahuje pouze jednu hodnotu "2"), když je určená[1, 2](kolekce se dvěma prvky).
Z tohoto důvodu se domníváme, že kvůli podpoře libovolných argumentů je potřeba srozumitelnější syntaxe, abychom tyto hodnoty jasněji omarili. V tomto prostoru se také objeví několik dalších aspektů návrhu. V žádném konkrétním pořadí:
Že řešení není nejednoznačné a způsobuje přerušení kódu, který lidé pravděpodobně používají s výrazy kolekce dnes. Například:
List<Widget> c = [new(...), w1, w2, w3];To je v současné chvíli legální a
new(...)výraz je "implicitní vytvoření objektu", který vytvoří nový widget. Tento účel nemůžeme změnit tak, aby předával argumenty konstruktoruList<>, protože by určitě přerušil existující kód.Tato syntaxe se nevztahuje mimo
[...]konstruktor. Například:HashSet<string> s = [...] with ...;Tyto syntaxe lze považovat za první, což znamená, že se kolekce vytvoří jako první, a pak se znovu vytvoří v odlišné formě, což znamená několik transformací dat a potenciálně nežádoucí vyšší náklady (i když se nevygeneruje).
To
newjako potenciální klíčové slovo, které se v tomto prostoru vůbec používá, je nesmirně matoucí. Obojí, protože[...]již označuje, že je vytvořen nový objekt, a protože překlady výrazu kolekce mohou procházet rozhraníMI API bez konstruktoru (například vzor metody Create ).Že řešení není příliš podrobné. Návrh základních hodnot výrazů kolekce je stručný. Pokud tedy formulář přidá velké množství syntaktického generování, bude to vypadat jako krok zpět a podkope hodnotu použití výrazů kolekce a volání do existujících rozhraní API, aby se kolekce vytvořila.
Všimněte si, že syntaxe, jako new([...], ...) je spuštění afoul "2" a "3" výše. Zdá se, že voláme do konstruktoru (když nemusíme být) a znamená to, že vytvořený výraz kolekce je předán tomuto konstruktoru, což rozhodně není.
NazákladěchCHch
[with(...arguments...)] Design
Syntax:
collection_element
: expression_element
| spread_element
+ | with_element
;
+with_element
+ : 'with' argument_list
+ ;
V této gramatické produkci se okamžitě zavádí syntaktická nejednoznačnost. Podobně jako nejednoznačnost mezi spread_element a expression_element(zde je vysvětleno, existuje okamžitá syntaktická nejednoznačnost mezi with_element a expression_element.
with(<arguments>) Konkrétně je přesně výrobní tělo pro with_element, a je také dosažitelný prostřednictvím expression_element -> expression -> ... -> invocation_expression. Pro collection_elements existuje jednoduché nadlimitní pravidlo. Konkrétně, pokud element lexicky začíná s posloupností with( tokenů, je vždy považován za with_element.
To je výhodné dvěma způsoby. Za prvé, implementace kompilátoru se musí podívat pouze na bezprostředně následující tokeny, které vidí určit, jaký druh elementu se má parsovat. Za druhé, odpovídajícím způsobem může uživatel triviálně pochopit, jaký druh prvku má, aniž by se museli mentálně snažit parsovat, co následuje, aby zjistili, zda by si to měli představit jako nebo with_element jako expression_element.
Příklady
Příklady, jak by to vypadalo:
// With an existing type:
// Initialize to twice the capacity since we'll have to add
// more values later.
List<string> names = [with(capacity: values.Count * 2), .. values];
Zdá se, že tyto formuláře "čtou" přiměřeně dobře. Ve všech těchto případech je kód "vytvoření výrazu kolekce" s následujícími argumenty, které se mají předat k řízení konečné instance, a následné prvky použité k naplnění. Například první řádek "vytvoří seznam řetězců "s" kapacitou dvakrát krát počet hodnot, které se mají rozdělit do něj"
Důležité je, že tento kód má malou šanci na přehlédnutí jako u formulářů, jako jsou: [arg; element], a zároveň přidává minimální úroveň podrobností, s velkou flexibilitou pro předávání požadovaných argumentů.
Technicky by to byla zásadní změna, protože with(...)by mohlo být voláním již existující metody volané with. Na rozdíl od new(...)známého a doporučeného způsobu vytváření implicitně zadaných hodnot with(...) je však mnohem méně pravděpodobné jako název metody, kdy se spustí afoul názvů .Net pro metody. V nepravděpodobném případě, že uživatel takovou metodu měl, by jistě mohl pokračovat v volání do existující metody pomocí @with(...).
Tento prvek bychom přeložili with(...) takto:
List<string> names = [with(/*capacity*/10), ...]; // translates to:
// argument_list *becomes* the argument list for the
// constructor call.
__result = new List<string>(10); // followed by normal initialization
// or
IList<string> names2 = [with(capacity: 20), ...]; // translates to:
__result = new List<string>(20);
Jinými slovy, argument_list argumenty by byly předány příslušnému konstruktoru, pokud voláme konstruktor, nebo do vhodné metody create, pokud takovou metodu voláme. Také bychom umožnili, aby se při vytváření instance jednoho z typů rozhraní cílového slovníku při vytváření instance jednoho z typů rozhraní cílového slovníku poskytl jediný argument, který dědí z typů porovnání seznamů BCL, aby bylo možné řídit jeho chování.
Převody
Oddíl převodů pro výrazy kolekce se aktualizuje následujícím způsobem:
> A struct or class type that implements System.Collections.IEnumerable where:
- * The type has an applicable constructor that can be invoked with no arguments, and the constructor is accessible at the location of the collection expression.
+ a. the collection expression has no `with_element` and the type has an applicable constructor
+ that can be invoked with no arguments, accessible at the location of the collection expression. or
+ b. the collection expression has a `with_element` and the type has at least one constructor
+ accessible at the location of the collection expression.
Všimněte si skutečných argumentů v rámci objektu argument_listwith_element , který nemá vliv na to, zda převod existuje nebo neexistuje. Jen přítomnost nebo nepřítomnost samotného with_element . Citlivost zde je jednoduchá, že pokud je výraz kolekce napsán bez jednoho (například [x, y, z]), musí být schopen volat konstruktor bez args. I když je [with(...), x, y, z] v něm, může volat příslušný konstruktor. To také znamená, že typy, které nelze vyvolat s konstruktorem bez argumentu , lze použít s výrazem kolekce, ale pouze v případě, že výraz kolekce, který obsahuje with_element.
Skutečné určení toho, jak bude mít vliv na with_element výstavbu, je uvedeno níže.
Konstrukce
Konstrukce se aktualizuje následujícím způsobem.
Prvky výrazu kolekce se vyhodnocují v pořadí, zleva doprava. V rámci argumentů kolekce se argumenty vyhodnocují v pořadí, zleva doprava. Každý prvek nebo argument se vyhodnotí přesně jednou a všechny další odkazy odkazují na výsledky tohoto počátečního vyhodnocení.
Pokud je součástí collection_arguments a není prvním prvkem ve výrazu kolekce, zobrazí se chyba v době kompilace.
Pokud seznam argumentů obsahuje jakékoli hodnoty s dynamickým typem, zobrazí se chyba v době kompilace (LDM-2025-01-22).
Konstruktory
Pokud cílový typ je struktura nebo typ třídy , který implementuje System.Collections.IEnumerable, a cílový typ nemá metodu create, a cílový typ není obecný typ parametru pak:
- Rozlišení přetížení se používá k určení nejlepšího konstruktoru instance z kandidátů.
- Sada konstruktorů kandidáta je všechny přístupné konstruktory instance deklarované v cílovém typu, které jsou použitelné s ohledem na seznam argumentů definované v příslušném členu funkce.
- Pokud je nalezen nejlepší konstruktor instance, konstruktor je vyvolán se seznamem argumentů.
- Pokud má konstruktor
paramsparametr, může být vyvolání v rozbalené podobě.
- Pokud má konstruktor
- V opačném případě se zobrazí chyba vazby.
// List<T> candidates:
// List<T>()
// List<T>(IEnumerable<T> collection)
// List<T>(int capacity)
List<int> l;
l = [with(capacity: 3), 1, 2]; // new List<int>(capacity: 3)
l = [with([1, 2]), 3]; // new List<int>(IEnumerable<int> collection)
l = [with(default)]; // error: ambiguous constructor
Metody CollectionBuilderAttribute
Pokud je cílovým typem typ s metodou create, pak:
- Řešení přetížení se používá k určení nejlepší metody vytvoření z kandidátů.
- Pro každou metodu vytvoření pro cílový typ definujeme metodu projekce s identickým podpisem metody create, ale bez posledního parametru.
- Sada metod projekce kandidáta je metody projekce , které se vztahují k seznamu argumentů , jak je definováno v příslušném členu funkce.
- Pokud je nalezena nejlepší metoda projekce, je vyvolána odpovídající metoda create se seznamem argumentů připojeným s
ReadOnlySpan<T>obsahujícími prvky. - V opačném případě se zobrazí chyba vazby.
[CollectionBuilder(typeof(MyBuilder), "Create")]
class MyCollection<T> { ... }
class MyBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> elements);
public static MyCollection<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> elements);
}
MyCollection<string> c1 = [with(GetComparer()), "1", "2"];
// IEqualityComparer<string> _tmp1 = GetComparer();
// ReadOnlySpan<string> _tmp2 = ["1", "2"];
// c1 = MyBuilder.Create<string>(_tmp1, _tmp2);
MyCollection<string> c2 = [with(), "1", "2"];
// ReadOnlySpan<string> _tmp3 = ["1", "2"];
// c2 = MyBuilder.Create<string>(_tmp3);
CollectionBuilderAttribute: Vytvoření metod
Pro výraz kolekce, kde definice cílového [CollectionBuilder] typu má atribut, jsou metody create následující, aktualizovány z výrazů kolekce: create metody.
Atribut
[CollectionBuilder(...)]určuje typ tvůrce a název metody , které se mají vyvolat k vytvoření instance typu kolekce.Typ tvůrce musí být ne generický
classnebostruct.Nejprve je určena sada použitelných metod
CMvytvoření. Skládá se z metod, které splňují následující požadavky:
- Metoda musí mít název zadaný v atributu
[CollectionBuilder(...)].- Metoda musí být definována přímo u typu tvůrce .
- Metoda musí být
static.- Metoda musí být přístupná tam, kde se používá výraz kolekce.
- Arity metody musí odpovídat arity typu kolekce.
- Metoda musí mít poslední parametr typu
System.ReadOnlySpan<E>, předaný hodnotou.- Existuje převod identity, implicitní převod odkazu nebo boxing převodu z metody návratový typ na typ kolekce.
Metody deklarované na základních typech nebo rozhraních se ignorují a nejsou součástí
CMsady.
Pro výraz kolekce s cílovým typem
C<S0, S1, …>, kde deklaraceC<T0, T1, …>typu má přidruženou metoduB.M<U0, U1, …>()tvůrce, se na metodu tvůrce použijí argumenty obecného typu z cílového typu v pořadí – a od nejkrajnějšího typu obsahujícího typ nejvíce dovnitř – do metody tvůrce.
Hlavní rozdíly oproti předchozímu algoritmu jsou:
- Metody vytvoření mohou mít před parametrem
ReadOnlySpan<E>další parametry. - Podporuje se více metod vytváření.
Typ cíle rozhraní
Pokud je cílovým typem typ rozhraní, pak:
K určení nejlepšího podpisu metody kandidáta se používá rozlišení přetížení.
Sada kandidátských podpisů je podpisy níže pro cílové rozhraní, které se vztahují k seznamu argumentů , jak je definováno v příslušném členu funkce.
Interfaces Kandidátské podpisy IEnumerable<E>IReadOnlyCollection<E>IReadOnlyList<E>()(žádné parametry)ICollection<E>IList<E>List<E>()List<E>(int)
Pokud se najde nejlepší podpis metody, sémantika je následující:
- Kandidát podpis pro
IEnumerable<E>,IReadOnlyCollection<E>aIReadOnlyList<E>je jednoduše()a má stejný význam jako nemáwith()prvek vůbec. - Kandidátské podpisy a
IList<T>ICollection<T>jsou podpisyList<T>()aList<T>(int)konstruktory. Při vytváření hodnoty (viz Mutable Interface Translation), bude vyvolán příslušnýList<T>konstruktor. - V opačném případě se zobrazí chyba vazby.
typ cíle Dictionary-Interface
Toto je zde uvedeno jako součást funkce definované v https://github.com/dotnet/csharplang/blob/main/proposals/dictionary-expressions.md.
Výše uvedený seznam je rozšířen tak, aby měl následující položky:
| Interfaces | Kandidátské podpisy |
|---|---|
IReadOnlyDictionary<K, V> |
() (žádné parametry)(IEqualityComparer<K>? comparer) |
IDictionary<K, V> |
Dictionary<K, V>()Dictionary<K, V>(int)Dictionary<K, V>(IEqualityComparer<K>)Dictionary<K, V>(int, IEqualityComparer<K>) |
Pokud se najde nejlepší signatura metody, bude následovat sémantika:
- Kandidátské podpisy
IReadOnlyDictionary<K, V>jsou()(které mají stejný význam jako prvek vůbec)with()a(IEqualityComparer<K>). Tento porovnávač se použije k odpovídající hodnotě hash a porovnání klíčů v cílovém slovníku, který kompilátor zvolí k vytvoření (viz Nesměnitelný překlad rozhraní). - Kandidátské podpisy
IDictionary<T>jsou podpisyDictionary<K, V>(),Dictionary<K, V>(int)Dictionary<K, V>(IEqualityComparer<K>)aDictionary<K, V>(int, IEqualityComparer<K>)konstruktory. Při vytváření hodnoty (viz Mutable Interface Translation), bude vyvolán příslušnýDictionary<K, V>konstruktor. - V opačném případě se zobrazí chyba vazby.
IDictionary<string, int> d;
IReadOnlyDictionary<string, int> r;
d = [with(StringComparer.Ordinal)]; // new Dictionary<string, int>(StringComparer.Ordinal)
r = [with(StringComparer.Ordinal)]; // new $PrivateImpl<string, int>(StringComparer.Ordinal)
d = [with(capacity: 2)]; // new Dictionary<string, int>(capacity: 2)
r = [with(capacity: 2)]; // error: 'capacity' parameter not recognized
d = [with()]; // Legal: empty arguments supported for interfaces
Další cílové typy
Pokud je cílový typ jakýkoli jiný typ, zobrazí se pro seznam argumentů chyba vazby, i když je prázdný.
Span<int> a = [with(), 1, 2, 3]; // error: arguments not supported
Span<int> b = [with([1, 2]), 3]; // error: arguments not supported
int[] a = [with(), 1, 2, 3]; // error: arguments not supported
int[] b = [with(length: 1), 3]; // error: arguments not supported
Bezpečnost referencí
Upravíme pravidla collection-expressions.md#ref-safety tak, aby zohlednila with() prvek.
Viz také §16.4.15 Omezení bezpečného kontextu.
Vytvoření metod
Tato část se vztahuje na výrazy kolekce, jejichž cílový typ splňuje omezení definovaná v metodách CollectionBuilderAttribute.
Bezpečný kontext je určen úpravou klauzule z collection-expressions.md#ref-safety (změny tučně):
- Pokud je cílovým typem ref typ struktury s metodou create, bezpečný kontext výrazu kolekce je bezpečný kontext vyvolání metody create, kde argumenty jsou
with()argumenty elementu argumenty následované výrazem kolekce jako argument pro poslední parametr (ReadOnlySpan<E>parametr).
Argumenty metody musí odpovídat omezení platí pro výraz kolekce. Podobně jako výše uvedené stanovení bezpečného kontextumusí argumenty metody odpovídat omezení je použito tím, že se s výrazem kolekce považuje za vyvolání metody create, kde argumenty jsou with() argumenty elementu následované výrazem kolekce jako argumentem posledního parametru.
Volání konstruktoru
Tato část se vztahuje na výrazy kolekce, jejichž cílový typ splňuje omezení definovaná v konstruktorech.
Výraz kolekce typu ref struktury následujícího formuláře:
[with(a₁, a₂, ..., aₙ), e₁, e₂, ..., eₙ]
Bezpečný kontext výrazu kolekce je nejužší z bezpečných kontextů následujících výrazů:
- Výraz
new C(a₁, a₂, ..., aₙ)vytvoření objektu , kdeCje cílový typ - Výrazy elementu (buď samotné výrazy
e₁, e₂, ..., eₙ, nebo hodnotu rozpětí v případě elementu spread).
Argumenty metody musí odpovídat omezení platí pro výraz kolekce. Omezení se použije tak, že se s výrazem kolekce zachází jako s objektem, který vytváří formulář new C(a₁, a₂, ..., aₙ) { e₁, e₂, ..., eₙ } podle low-level-struct-improvements.md#rules-for-object-initializers.
- Elementy výrazu jsou považovány za inicializátory elementů kolekce.
- Prvky šíření jsou zpracovávány podobně, dočasně za předpokladu, že
Cmá metoduAdd(SpreadType spread), kdeSpreadTypeje typ hodnoty rozpětí.
Zodpovězené otázky
dynamic argumenty
Mají být argumenty s typem dynamic povolené? To může vyžadovat použití pořadače modulu runtime pro řešení přetížení, což by ztěžovalo omezení sady kandidátů, například pro případy tvůrce kolekcí.
Rozlišení: Zakázány. LDM-2025-01-22
with() změna způsobující chybu
Navrhovaný with() prvek je zásadní změna.
object x, y, z = ...;
object[] items = [with(x, y), z]; // C#13: ok; C#14: error args not supported for object[]
object with(object x, object y) { ... }
Ověřte, že změna způsobující chybu je přijatelná a jestli by se změna způsobující chybu měla svázat s jazykovými verzemi.
Rozlišení: Při kompilaci s dřívější jazykovou verzí ponechte předchozí chování (bez zásadní změny). LDM-2025-03-17
Mají argumenty vliv na převod výrazů kolekce?
Mají argumenty kolekce a příslušné metody vliv na převod výrazu kolekce?
Print([with(comparer: null), 1, 2, 3]); // ambiguous or Print<int>(HashSet<int>)?
static void Print<T>(List<T> list) { ... }
static void Print<T>(HashSet<T> set) { ... }
Pokud argumenty ovlivňují převodnost na základě použitelných metod, měly by argumenty pravděpodobně ovlivnit i odvození typu.
Print([with(comparer: StringComparer.Ordinal)]); // Print<string>(HashSet<string>)?
Podobné případy s cílovým typem new() vedou k chybám.
Print<int>(new(comparer: null)); // error: ambiguous
Print(new(comparer: StringComparer.Ordinal)); // error: type arguments cannot be inferred
Rozlišení: Argumenty kolekce by se měly ignorovat při převodech a odvození typu. LDM-2025-03-17
Pořadí parametrů metody Tvůrce kolekcí
U metod tvůrce kolekcí by měl být parametr span před nebo za libovolnými parametry pro argumenty kolekce?
Prvky by nejprve umožnily deklarovat argumenty jako volitelné.
class MySetBuilder
{
public static MySet<T> Create<T>(ReadOnlySpan<T> items, IEqualityComparer<T> comparer = null) { ... }
}
Argumenty by nejprve umožňovaly, aby rozsah byl params parametrem, který podporuje volání přímo v rozšířené podobě.
var s = MySetBuilder.Create(StringComparer.Ordinal, x, y, z);
class MySetBuilder
{
public static MySet<T> Create<T>(IEqualityComparer<T> comparer, params ReadOnlySpan<T> items) { ... }
}
Rozlišení: Parametr span pro elementy by měl být posledním parametrem. LDM-2025-03-12
Argumenty s dřívější jazykovou verzí
Je při with() kompilaci se starší jazykovou verzí hlášena chyba nebo se with sváže s jiným symbolem v oboru?
Rozlišení: Při kompilaci s dřívějšími jazykovými verzemi se v rámci výrazu kolekce nemění žádná změna způsobující chybu with .
LDM-2025-03-17
Cílové typy, ve kterých jsou požadovány argumenty
Mají být převody výrazů kolekce podporovány na cílové typy, kde musí být zadány argumenty, protože všechny konstruktory nebo metody továrny vyžadují alespoň jeden argument?
Takové typy lze použít s výrazy kolekce, které obsahují explicitní with() argumenty, ale typy nelze použít pro params parametry.
Představte si například následující typ vytvořený z metody továrny:
MyCollection<object> c;
c = []; // error: no arguments
c = [with(capacity: 1)]; // ok
[CollectionBuilder(typeof(MyBuilder), "Create")]
class MyCollection<T> : IEnumerable<T> { ... }
class MyBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, int capacity) { ... }
}
Stejná otázka platí pro případ, kdy je konstruktor volána přímo jako v následujícím příkladu.
Nicméně, pro cílové typy, kde je konstruktor volán přímo, převod výrazu kolekce v současné době vyžaduje konstruktor volatelný bez argumentů, ale argumenty kolekce jsou ignorovány při určování convertibility.
c = []; // error: no arguments
c = [with(capacity: 1)]; // error: no constructor callable with no arguments?
class MyCollection<T> : IEnumerable<T>
{
public MyCollection(int capacity) { ... }
public void Add(T t) { ... }
// ...
}
Rozlišení: Podpora převodů na cílové typy, kde všechny konstruktory nebo metody továrny vyžadují argumenty a vyžadují with() převod.
LDM-2025-03-05
__arglist
Měly by __arglist být podporovány v with() prvech?
class MyCollection : IEnumerable
{
public MyCollection(__arglist) { ... }
public void Add(object o) { }
}
MyCollection c;
c = [with(__arglist())]; // ok
c = [with(__arglist(x, y)]; // ok
Rozlišení:__arglist Nepodporuje se v argumentech kolekce, pokud není zadarmo.
LDM-2025-03-05
Argumenty pro typy rozhraní
Měly by být argumenty podporované pro cílové typy rozhraní?
ICollection<int> c = [with(capacity: 4)];
IReadOnlyDictionary<string, int> d = [with(comparer: StringComparer.Ordinal), ..values];
U proměnlivých typů rozhraní jsou k dispozici následující možnosti:
- Používejte přístupné konstruktory z dobře známého typu potřebného pro vytvoření instance:
List<T>neboDictionary<K, V>. - Používejte podpisy nezávislé na konkrétním typu, například použití
new()anew(int capacity)proICollection<T>aIList<T>(viz Konstrukce pro potenciální podpisy pro každé rozhraní).
Použití přístupných konstruktorů z dobře známého typu má následující důsledky:
- Názvy parametrů, nepovinnost,
paramsjsou převzaty přímo z parametrů. - Zahrnuty jsou všechny přístupné konstruktory, i když to nemusí být užitečné pro výrazy kolekce, jako
List(IEnumerable<T>)by to umožniloIList<int> list = [with(1, 2, 3)];. - Sada konstruktorů může záviset na verzi seznamu BCL.
Recomendation: Use the accessible constructors from the well-known types. Zaručujeme, že bychom tyto typy použili, takže se jedná o nejjasnější a nejjednodušší cestu k vytvoření těchto hodnot.
U neměnitelných typů rozhraní jsou možnosti podobné:
- Neprovádět žádnou akci. Toto
- Používejte podpisy nezávislé na konkrétním typu, i když jediný scénář může být
new(IEqualityComparer<K> comparer)proIReadOnlyDictionary<K, V>C#14..
Použití přístupných konstruktorů z některého dobře známého typu (strategie pro proměnlivé typy rozhraní) není možné, protože neexistuje žádný vztah k žádnému konkrétnímu existujícímu typu a konečný typ, který můžeme použít nebo syntetizovat. Proto by musely být liché nové požadavky, které kompilátor dokáže mapovat jakýkoli existující konstruktor tohoto typu (i když se vyvíjí) na nesměnitelnou instanci, kterou ve skutečnosti generuje.
Recomendation: Use signatures independent of a specific type. A pro jazyk C# 14 platí new(IEqualityComparer<K> comparer)IReadOnlyDictionary<K, V> , že je to jediné nesměnitelné rozhraní, u kterého máme pocit, že je důležité pro použitelnost/sémantiku, aby uživatelé mohli tuto funkci poskytnout. Budoucí verze jazyka C# mohou zvážit rozšíření této sady na základě pevných odůvodnění.
Řešení:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-04-23.md
Argumenty jsou podporovány pro cílové typy rozhraní. U proměnlivých i nesměnitelných rozhraní bude sada argumentů kurátorovaná.
Očekávaný seznam (který stále musí být ratifikován) je cílový typ rozhraní.
Prázdné seznamy argumentů
Měli bychom povolit prázdné seznamy argumentů pro některé nebo všechny cílové typy?
Prázdné with() by odpovídalo ne with(). Může poskytovat určitou konzistenci s neprázdnými případy, ale nepřidá žádné nové funkce.
List<int> l = [with()]; // ok? new List<int>()
ImmutableArray<int> m = [with()]; // ok? ImmutableArray.Create<int>()
IList<int> i = [with()]; // ok? new List<int>() or equivalent
IEnumerable<int> e = [with()]; // ok?
int[] a = [with()]; // ok?
Span<int> s = [with()]; // ok?
Řešení:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-05-12.md#empty-argument-lists
U typů konstruktorů a typů tvůrce, které je možné volat bez argumentů, povolíme funkci with() a pro typy rozhraní (proměnlivé a jen pro čtení) přidáme prázdné podpisy konstruktoru. Pole a rozsahy nebudou povoleny pomocí (), protože neexistují žádné podpisy, které by se jim vešly.
Otevřené otázky
Finalizace otevřeného problému z https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-03-17.md#conclusion
with(...) je zásadní změna jazyka s [with(...)]. Před touto funkcí to znamená výraz kolekce s jedním prvkem, což je výsledek volání with-invocation-expression. Po této funkci se jedná o kolekci, která má předané argumenty.
Chceme, aby k tomuto přerušení docházelo jenom v případě, že uživatel vybere konkrétní jazykovou verzi (například C#-14/15?). Jinými slovy, pokud jsou ve starší verzi jazyka, získají předchozí logiku analýzy, ale v novější verzi získají novější logiku analýzy. Nebo chceme, aby měla novější logiku analýzy, a to i u staršího jazyka?
Máme předchozí umění pro obě strategie.
required, například, je vždy analyzován s novou logikou bez ohledu na langversion.
record/field Zatímco zatímco ostatní mění logiku analýzy v závislosti na jazykové verzi.
A konečně, to má překrývající se a vliv s Dictionary Expressions, který zavádí key:value syntaxi pro KVP-elementy. Chceme vytvořit chování, které chceme pro libovolnou verzi jazyka, a pro [with(...)] vlastní a věci jako [with(...) : expr] nebo [expr : with(...)].
C# feature specifications