Gyűjteménykifejezés argumentumai

Note

Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.

A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.

A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.

A bajnokkal kapcsolatos kérdés: https://github.com/dotnet/csharplang/issues/8887

Motiváció

A szótárkifejezési funkció azt észlelte, hogy a gyűjteménykifejezéseknek a felhasználó által megadott adatok mentén kell továbbítaniuk a végső gyűjtemény viselkedésének konfigurálásához. A szótárak lehetővé teszik a felhasználók számára a kulcsok összehasonlításának testreszabását, a kulcsok közötti egyenlőség definiálását, valamint a rendezést vagy kivonatolást (rendezett vagy kivonatolt gyűjtemények esetén). Ez akkor szükséges, ha bármilyen szótártípust (például D d = new D(...), D d = D.CreateRange(...) sőt IDictionary<...> d = <synthesized dict>) hoz létre

Ennek támogatásához a rendszer egy új with(...arguments...) elemet javasol egy gyűjteménykifejezés első elemeként:

Dictionary<string, int> nameToAge = [with(comparer), .. d1, .. d2, .. d3];
  1. A hívásra való new CollectionType(...) fordításkor ezek ...arguments... határozzák meg a megfelelő konstruktort, és ennek megfelelően továbbítják őket.
  2. Hívásra való CollectionFactory.Create fordításkor a rendszer ezeket ...arguments... az elemek argumentumával ReadOnlySpan<ElementType> továbbítja, amelyek mindegyike a megfelelő Create túlterhelés meghatározására szolgál, és ennek megfelelően továbbítja őket.
  3. Interfészre (például IDictionary<,>) történő fordításkor csak egyetlen argumentum engedélyezett. Implementálja az egyik jól ismert BCL összehasonlító felületet, és a végső példány szemantikáját összehasonlító kulcs szabályozására szolgál.

Ez a szintaxis lett kiválasztva:

  1. Az összes információ a szintaxison [...] belül marad. Annak biztosítása, hogy a kód továbbra is egyértelműen jelezze a gyűjtemény létrehozását.
  2. Ez nem azt jelenti, hogy konstruktort new hív meg (ha nem így jön létre az összes gyűjtemény).
  3. Nem jelenti a gyűjtemény értékeinek többszöri létrehozását/másolását (például egy utótagot with { ... } ).
  4. Nem konzisztens a műveletek sorrendje, különösen a C#konzisztens balról jobb oldali kifejezés-kiértékelési sorrendjének szemantikája esetén. Például nem értékeli ki a gyűjtemény létrehozásához használt argumentumokat a gyűjtemény feltöltéséhez használt kifejezések kiértékelése után .
  5. Nem kényszeríti a felhasználót arra, hogy egy (potenciálisan nagy méretű) gyűjteménykifejezés végéig olvassa az olvasást az alapvető viselkedési szemantikák meghatározásához. Ha például egy százsoros szótár végére kellett néznie, csak hogy megtalálja, igen, a megfelelő kulcs-összehasonlítót használta.
  6. Mindkettő nem finom, ugyanakkor nem túl részletes. Az argumentumok megjelölése ; helyett például , nagyon egyszerű szintaxist kell kihagyni. with() csak 6 karaktert ad hozzá, és könnyen kiemelkedik, különösen a with kulcsszó szintaxisának színezésével.
  7. Szépen olvas. "Ez egy gyűjteménykifejezés, amely ezeket az argumentumokat tartalmazza, és ezekből az elemekből áll."
  8. Megoldja a szótárak és a készletek összehasonlító szükségletét.
  9. Biztosítja, hogy minden felhasználónak szüksége van az argumentumok átadására, vagy hogy a jövőben az összehasonlítókon túli igényeink már kezelhetők legyenek.
  10. Nem ütközik meglévő kóddal (keresésre).https://grep.app/

Tervezési filozófia

Az alábbi szakasz a korábbi tervezési filozófiai megbeszéléseket ismerteti. Beleértve azt is, hogy miért utasítottak el bizonyos űrlapokat.

Ennek a felhasználó által definiált adatoknak két fő irányt adhatunk meg. Az első az, hogy csak a összehasonlító térben lévő értékeket kell speciálisan megadni (amelyeket a BCL-ek IComparer<T> vagy IEqualityComparer<T> -típusok által öröklő típusokként határozunk meg). A második egy általánosított mechanizmus biztosítása, amely tetszőleges argumentumokat biztosít a végső meghívott API-nak gyűjteménykifejezések létrehozásakor. Az elsődleges szótárkifejezés specifikációja bemutatja, hogyan lehet az előbbit elvégezni, míg ez a specifikáció az utóbbit keresi.

Az összehasonlítók megoldásainak vizsgálatai gyengeségeket tártak fel a megközelítésükben, ha tetszőleges érvekre szeretnénk kiterjeszteni őket. Például:

  1. Elemszintaxis újrahasználása, ahogyan az űrlapon: [StringComparer.OrdinalIgnoreCase, "mads": 21]. Ez jól működik egy olyan térben, ahol KeyValuePair<,> a összehasonlítók nem örökölnek a gyakori típusoktól. De letörik egy olyan világban, ahol lehet, hogy: HashSet<object> h = [StringComparer.OrdinalIgnoreCase, "v"]. Ez egy összehasonlító mentén halad? Vagy megpróbál két objektumértéket elhelyezni a készletben?

  2. Argumentumok és finom szintaxisú elemek elválasztása (például pontosvessző helyett pontosvesszővel elválasztani őket [comparer; v1]). Ez nagyon zavaró helyzeteket rejt magában, amikor egy felhasználó véletlenül ír [1; 2] (és olyan gyűjteményt kap, amely az "1" értéket adja át, például a "kapacitás" argumentumot egy List<>, és csak a "2" egyetlen értéket tartalmazza), amikor azt tervezték [1, 2] (két elemből álló gyűjtemény).

Emiatt az tetszőleges argumentumok támogatásához úgy gondoljuk, hogy nyilvánvalóbb szintaxisra van szükség az értékek egyértelműbb kijelöléséhez. Ezen a téren számos más tervezési szempont is felmerült. Bizonyos sorrendben ezek a következők:

  1. Hogy a megoldás nem egyértelmű, és olyan kódtöréseket okoz, amelyeket a felhasználók valószínűleg a gyűjteménykifejezésekkel használnak. Például:

    List<Widget> c = [new(...), w1, w2, w3];
    

    Ez ma már legális, mivel a new(...) kifejezés egy "implicit objektum létrehozása", amely létrehoz egy új widgetet. Ezt nem tudjuk arra visszavenni, hogy argumentumokat továbbítsunk a konstruktornak List<>, mivel az biztosan megszakítaná a meglévő kódot.

  2. Hogy a szintaxis nem terjed ki a [...] szerkezeten kívülre. Például:

    HashSet<string> s = [...] with ...;
    

    Ezek a szintaxisok úgy értelmezhetők, hogy a gyűjtemény először létrejön, majd újra létrejön egy eltérő formában, ami az adatok többszörös átalakítását jelenti, és potenciálisan nem kívánt magasabb költségeket eredményez (még akkor is, ha ez nem az, amit a rendszer bocsát ki).

  3. Ez new , mint egy potenciális kulcsszó, hogy egyáltalán ebben a térben nem kívánatos zavaró. Mindkettő azért, mert [...]már jelzi, hogy létrejön egy új objektum, és mivel a gyűjteménykifejezés fordításai nem konstruktor API-kon (például a Létrehozási metódusmintán ) keresztül mennek keresztül.

  4. Hogy a megoldás ne legyen túlzottan részletes. A gyűjteménykifejezések alapvető értékajánlata a rövidség. Tehát ha az űrlap nagy mennyiségű szintaktikai állványzatot ad hozzá, az visszalépésnek fog tűnni, és aláássa a gyűjteménykifejezések használatának értékét, szemben a meglévő API-k meghívásával a gyűjtemény létrehozásához.

Vegye figyelembe, hogy egy ilyen new([...], ...) szintaxis a fenti "2" és "3" értékre is érvényes. Úgy jelenik meg, mintha konstruktort hívnánk meg (ha nem vagyunk), és azt jelenti, hogy egy létrehozott gyűjteménykifejezést ad át a konstruktornak, ami biztosan nem.

A fentiek alapján néhány olyan lehetőség jött létre, amelyek úgy érzik, hogy megoldják az argumentumok átadásának igényeit anélkül, hogy túllépnék a gyűjteménykifejezések céljainak határait.

[with(...arguments...)] Tervezés

Syntax:

collection_element
   : expression_element
   | spread_element
+  | with_element
   ;

+with_element
+  : 'with' argument_list
+  ;

Ezzel a nyelvtani termeléssel azonnal megjelenik egy szintaktikai kétértelműség. Hasonló a kétértelműség és spread_elementexpression_element ( itt magyarázható, van egy azonnali szintaktikai kétértelműség között with_element és expression_element. Konkrétan with(<arguments>) mindkettő pontosan a termelési törzs, with_elementés ez is elérhető keresztül expression_element -> expression -> ... -> invocation_expression. A collection_elements egyszerű átfogó szabálya van. Pontosabban, ha az elem lexikálisan a jogkivonat-sorozattal with( kezdődik, akkor a rendszer mindig úgy kezeli, mint egy with_element.

Ez kétféleképpen előnyös. Először is a fordító implementációnak csak azokat az azonnal megjelenő jogkivonatokat kell megvizsgálnia, amelyek alapján megállapíthatja, hogy milyen elemet kell elemezni. Másodszor, ennek megfelelően a felhasználó triviálisan megértheti, hogy milyen elemük van, anélkül, hogy mentálisan meg kellene próbálnia elemezni az alábbiakat, hogy lássa, kell-e úgy gondolnia rá, mint egy with_element vagy egy expression_element.

Examples

Példák a következőkre:

// 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];

Ezek a formák úgy tűnik, hogy "olvasni" ésszerűen is. Ezekben az esetekben a kód "gyűjteménykifejezést hoz létre, a következő argumentumokkal együtt" a végső példány vezérléséhez, majd a feltöltéshez használt további elemeket. Az első sor például "olyan sztringlistát hoz létre, amelynek kapacitása kétszer akkora, mint a benne elosztandó értékek száma"

Fontos, hogy ennek a kódnak kevés esélye van arra, hogy figyelmen kívül hagyják, mint például a következő űrlapok: [arg; element], miközben minimális részletességet ad hozzá, nagy rugalmassággal, hogy minden kívánt argumentumot átadjon.

Ez gyakorlatilag egy kompatibilitástörő változás lenne, ahogy with(...) az egy már meglévő, úgynevezett metódus hívása with. Az implicit típusú értékek létrehozásának new(...) és ajánlott módjával ellentétben azonban sokkal kevésbé valószínű, with(...) hogy metódusnévként futtassa a .Net-elnevezést a metódusok esetében. Abban a valószínűtlen esetben, ha egy felhasználó rendelkezik ilyen metódussal, akkor minden bizonnyal továbbra is be tudja hívni a meglévő metódust a használatával @with(...).

Ezt az elemet a with(...) következőképpen fordítanánk le:

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

Más szóval, a argument_list argumentumokat átadjuk a megfelelő konstruktornak, ha konstruktort hívunk meg, vagy ha ilyen metódust hívunk meg, a megfelelő "létrehozási módszernek". Azt is lehetővé tenné, hogy a BCL-összehasonlító típusoktól öröklő egyetlen argumentum legyen megadva, amikor a célszótár-felület egyik típusának példányosításával szabályozni szeretné annak viselkedését.

Conversions

A gyűjteménykifejezések konverziós szakasza a következő módon frissül:

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

Figyelje meg, hogy a tényleges argumentumok argument_listwith_element nem befolyásolják, hogy az átalakítás létezik-e vagy sem. Csak önmagának a jelenléte vagy hiánya with_element . Az intuíció itt egyszerűen az, hogy ha a gyűjteménykifejezést egy (például [x, y, z]) nélkül írják, akkor a konstruktort args nélkül kell meghívni. Míg ha van [with(...), x, y, z] , akkor meghívhatja a megfelelő konstruktort. Ez azt is jelenti, hogy az argumentum nélküli konstruktorral nemhívható típusok gyűjteménykifejezéssel használhatók, de csak akkor, ha a gyűjteménykifejezés tartalmaz egy with_element.

Az with_element a tényleges meghatározást adhatja meg arról, hogy egy adott eszköz hogyan befolyásolja az építkezést.

Construction

Az építés az alábbiak szerint frissül.

A gyűjteménykifejezés elemeinek kiértékelése sorrendben történik, balról jobbra. A gyűjteményargumentumokon belül az argumentumok kiértékelése sorrendben történik, balról jobbra. Az egyes elemek vagy argumentumok kiértékelése pontosan egyszer történik, és a további hivatkozások a kezdeti értékelés eredményeire vonatkoznak.

Ha collection_arguments szerepel, és nem az első elem a gyűjteménykifejezésben, a jelentés fordítási időt jelző hibát jelez.

Ha az argumentumlistadinamikus típusú értékeket tartalmaz, a rendszer fordítási időt jelző hibát jelez (LDM-2025-01-22).

Constructors

Ha a céltípus egy implementálható struktúra vagy System.Collections.IEnumerable, és a céltípus nem rendelkezik létrehozási metódussal, és a céltípus nem általános paramétertípus, akkor:

  • A túlterhelés feloldása a jelöltek legjobb példánykonstruktorának meghatározására szolgál.
  • A jelölt konstruktorok készlete az összes elérhető példánykonstruktor, amelyet a céltípuson deklaráltak, és amelyek az alkalmazható függvénytagban meghatározott argumentumlistára vonatkoznak.
  • Ha a legjobb példánykonstruktor található, a rendszer meghívja a konstruktort az argumentumlistával.
    • Ha a konstruktor rendelkezik paraméterrel params , a meghívás kibontott formában is lehetséges.
  • Ellenkező esetben kötési hiba jelenik meg.
// 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

CollectionBuilderAttribute metódusok

Ha a céltípus egy létrehozási metódussal rendelkező típus, akkor:

  • A túlterhelés feloldása a jelöltek legjobb létrehozási módszerének meghatározására szolgál.
  • A céltípus minden létrehozási módszeréhez meghatározunk egy, a létrehozási metódussal azonos aláírású, de az utolsó paraméter nélküli vetítési metódust.
  • A jelölt vetítési módszerek halmaza azokat a vetítési módszereket tartalmazza, amelyek az alkalmazható függvénytagban meghatározott argumentumlistára vonatkoznak.
  • Ha a legjobb vetítési módszer található, a rendszer meghívja a megfelelő létrehozási metódust az elemeket tartalmazó argumentumlistávalReadOnlySpan<T>.
  • Ellenkező esetben kötési hiba jelenik meg.
[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: Metódusok létrehozása

Olyan gyűjteménykifejezések esetében, amelyekben a céltípus definíciója attribútummal rendelkezik [CollectionBuilder] , a létrehozási metódusok a következők, amelyek agyűjteménykifejezésekből frissülnek: metódusok létrehozása.

Az [CollectionBuilder(...)] attribútum a gyűjteménytípus egy példányának létrehozásához meghívandó metódus szerkesztőtípusát és metódusnevét adja meg.

A szerkesztő típusának nem általános class vagy struct.

Először meg kell határozni az alkalmazandó létrehozási módszerekCM készletét. Olyan módszerekből áll, amelyek megfelelnek a következő követelményeknek:

  • A metódusnak az attribútumban megadott névvel kell rendelkeznie [CollectionBuilder(...)] .
  • A metódust közvetlenül a szerkesztőtípuson kell definiálni.
  • A metódusnak a következőnek kell lennie static: .
  • A metódusnak elérhetőnek kell lennie a gyűjteménykifejezés használatakor.
  • A metódus aritásának meg kell egyeznie a gyűjteménytípus aritásával .
  • A metódusnak érték szerint átadott utolsó típusú System.ReadOnlySpan<E>paraméterrel kell rendelkeznie.
  • Identitáskonverzió, implicit referenciakonvertálás vagy boxing átalakítás történik a metódus visszatérési típusától a gyűjteménytípusig.

Az alaptípusokon vagy interfészeken deklarált metódusok figyelmen kívül lesznek hagyva, és nem részei a CM készletnek.

Olyan céltípusú C<S0, S1, …> esetében, ahol a típusdeklarációhozC<T0, T1, …> társított szerkesztőmetódusB.M<U0, U1, …>() tartozik, a céltípus általános típusargumentumait a rendszer a legbelső típustól a legbelsőig alkalmazza a szerkesztő metódusra.

A korábbi algoritmus főbb különbségei a következők:

  • A létrehozási metódusok további paraméterekkel is rendelkezhetnek a paraméter ReadOnlySpan<E>.
  • Több létrehozási módszer is támogatott.

Interfész céltípusa

Ha a céltípus egy felülettípus, akkor:

  • A túlterhelésfeloldás a legjobb jelöltmetódus-aláírás meghatározására szolgál.

  • A jelölt-aláírások halmaza az alábbi aláírások a célfelülethez, amelyek az alkalmazható függvénytagban meghatározott argumentumlistára vonatkoznak.

    Interfaces Jelölt-aláírások
    IEnumerable<E>
    IReadOnlyCollection<E>
    IReadOnlyList<E>
    () (nincsenek paraméterek)
    ICollection<E>
    IList<E>
    List<E>()
    List<E>(int)

Ha a legjobb módszeradékot találja, a szemantikák a következők:

  • A jelölt aláírása IEnumerable<E>, IReadOnlyCollection<E> és IReadOnlyList<E> egyszerűen () , és ugyanazt a jelentést, mintha egyáltalán nem rendelkezik az with() elem.
  • A jelölt aláírások IList<T>ICollection<T> a konstruktorok és List<T>() a konstruktorok aláírásaiList<T>(int). Az érték létrehozásakor (lásd: Mutable Interface Translation) a rendszer meghívja a megfelelő List<T> konstruktort.
  • Ellenkező esetben kötési hiba jelenik meg.

Dictionary-Interface céltípus

Ez itt van megadva a szakaszban definiált https://github.com/dotnet/csharplang/blob/main/proposals/dictionary-expressions.mdfunkció részeként.

A fenti lista a következő elemekkel bővül:

Interfaces Jelölt-aláírások
IReadOnlyDictionary<K, V> () (nincsenek paraméterek)
(IEqualityComparer<K>? comparer)
IDictionary<K, V> Dictionary<K, V>()
Dictionary<K, V>(int)
Dictionary<K, V>(IEqualityComparer<K>)
Dictionary<K, V>(int, IEqualityComparer<K>)

Ha a legjobb metódusa aláírást találja, a szemantikák a következők:

  • A jelölt aláírások IReadOnlyDictionary<K, V> ( () amelyek jelentése megegyezik azzal, hogy egyáltalán nem rendelkezik az with() elemet), és (IEqualityComparer<K>). Ez az összehasonlító a megfelelő kivonatoláshoz és a fordító által létrehozott célszótár kulcsainak összehasonlításához használható (lásd : Nem mutable Interface Translation).
  • A jelölt aláírások a IDictionary<T> , Dictionary<K, V>()Dictionary<K, V>(int) és Dictionary<K, V>(IEqualityComparer<K>)konstruktorok aláírásaiDictionary<K, V>(int, IEqualityComparer<K>). Az érték létrehozásakor (lásd: Mutable Interface Translation) a rendszer meghívja a megfelelő Dictionary<K, V> konstruktort.
  • Ellenkező esetben kötési hiba jelenik meg.
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

Egyéb céltípusok

Ha a céltípus bármilyen más típus, akkor a rendszer kötési hibát jelent az argumentumlistához, még akkor is, ha üres.

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

Bírók biztonsága

A collection-expressions.md#ref-safety szabályokat úgy módosítjuk, hogy figyelembe vegyék az with() elemet.

Lásd még a 16.4.15.

Metódusok létrehozása

Ez a szakasz olyan gyűjteménykifejezésekre vonatkozik, amelyek céltípusa megfelel a CollectionBuilderAttribute metódusokban meghatározott korlátozásoknak.

A biztonságos környezet meghatározása a collection-expressions.md#ref-safety záradék módosításával történik ( a módosítások félkövér nyelven):

  • Ha a céltípus egy létrehozási metódussal rendelkező refstruktúratípus, a gyűjteménykifejezés biztonságos környezete a létrehozási metódus meghívásának biztonságos környezete, ahol az argumentumok az with() elemargumentumok, majd a gyűjteménykifejezés az utolsó paraméter (a ReadOnlySpan<E> paraméter) argumentuma.

A metódusargumentumoknak meg kell egyeznie a gyűjteménykifejezésre vonatkozó korlátozással. A fenti biztonságos környezet meghatározásához hasonlóan a metódusargumentumoknak meg kell egyezniük a megszorítással, ha a gyűjteménykifejezést a létrehozási metódus meghívásaként kezelik, ahol az argumentumok az with() elemargumentumok, amelyeket a gyűjteménykifejezés követ az utolsó paraméter argumentumaként.

Konstruktorhívások

Ez a szakasz olyan gyűjteménykifejezésekre vonatkozik, amelyek céltípusa megfelel a Konstruktorokban meghatározott korlátozásoknak.

A következő űrlap ref strukturáltípusának gyűjteménykifejezéséhez:
[with(a₁, a₂, ..., aₙ), e₁, e₂, ..., eₙ]

A gyűjteménykifejezés biztonságos környezete a következő kifejezések biztonságos környezeteinek legszűkebb része :

  • Objektumlétrehozó kifejezés new C(a₁, a₂, ..., aₙ), ahol C a céltípus
  • Az elemkifejezések e₁, e₂, ..., eₙ (maguk a kifejezések, vagy az oldalpáros elem esetén az oldalpár értéke).

A metódusargumentumoknak meg kell egyeznie a gyűjteménykifejezésre vonatkozó korlátozással. A korlátozást úgy alkalmazza a rendszer, hogy a gyűjteménykifejezést az űrlap new C(a₁, a₂, ..., aₙ) { e₁, e₂, ..., eₙ } objektumlétrehozásaként kezeli a low-level-struct-improvements.md#rules-for-object-initializers fájlonként.

  • A kifejezéselemeket úgy kezeli a rendszer, mintha gyűjteményelem-inicializálók lennének.
  • Az oldalpár elemeit hasonlóan kezeli a rendszer, ha ideiglenesen feltételezi, hogy C van egy Add(SpreadType spread) metódusa, ahol SpreadType az oldalpár értékének típusa van.

Megválaszolt kérdések

dynamic Érvek

Engedélyezettek-e a típussal rendelkező dynamic argumentumok? Ehhez szükség lehet a futásidejű iratgyűjtő használatára a túlterhelés feloldásához, ami megnehezítené a jelöltek számának korlátozását, például a gyűjteménykészítő esetek esetében.

Felbontás: Nem engedélyezett. LDM-2025-01-22

with() kompatibilitástörő változás

A javasolt with() elem egy kompatibilitástörő változás.

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

Ellenőrizze, hogy a kompatibilitástörő változás elfogadható-e, és hogy a kompatibilitástörő módosításnak a nyelvi verzióhoz kell-e kötődnie.

Felbontás: A korábbi nyelvi verzió összeállításakor tartsa meg a korábbi viselkedést (nem változik kompatibilitástörő változás). LDM-2025-03-17

Hatással vannak-e az argumentumok a gyűjteménykifejezések konvertálására?

A gyűjtemény argumentumainak és az alkalmazható metódusoknak befolyásolják a gyűjteménykifejezés konvertálhatóságát?

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

Ha az argumentumok befolyásolják az átalakíthatóságot az alkalmazható módszerek alapján, az argumentumoknak valószínűleg a típusbeli következtetésre is hatással kell lennie.

Print([with(comparer: StringComparer.Ordinal)]); // Print<string>(HashSet<string>)?

Referenciaként a cél típusú new() esetekhez hasonló esetek hibákat eredményeznek.

Print<int>(new(comparer: null));              // error: ambiguous
Print(new(comparer: StringComparer.Ordinal)); // error: type arguments cannot be inferred

Felbontás: A gyűjteményargumentumokat figyelmen kívül kell hagyni a konvertálásokban és a típuskövetkeztetésben. LDM-2025-03-17

Gyűjteményszerkesztő metódus paraméterének sorrendje

A gyűjteményszerkesztő metódusok esetében a span paraméternek a gyűjteményargumentumok bármely paramétere előtt vagy után kell lennie?

Az elemek először lehetővé tennék az argumentumok opcionálisként való deklarálásához.

class MySetBuilder
{
    public static MySet<T> Create<T>(ReadOnlySpan<T> items, IEqualityComparer<T> comparer = null) { ... }
}

Az argumentumok először lehetővé tennék, hogy a span paraméter params legyen, és támogassa a közvetlenül kibontott formában történő hívásokat.

var s = MySetBuilder.Create(StringComparer.Ordinal, x, y, z);

class MySetBuilder
{
    public static MySet<T> Create<T>(IEqualityComparer<T> comparer, params ReadOnlySpan<T> items) { ... }
}

Felbontás: Az elemek span paraméterének kell lennie az utolsó paraméternek. LDM-2025-03-12

Argumentumok a korábbi nyelvi verzióval

Hiba jelenik with() meg egy korábbi nyelvi verzió összeállításakor, vagy with egy másik szimbólumhoz kapcsolódik a hatókörben?

Felbontás: A gyűjteménykifejezésen with belül nem változik kompatibilitástörő változás a korábbi nyelvi verziók fordításakor. LDM-2025-03-17

Céltípusok, ahol argumentumokra van szükség

Támogatja-e a gyűjteménykifejezések konvertálását olyan céltípusokra, ahol argumentumokat kell megadni, mert az összes konstruktor vagy gyári metódus legalább egy argumentumot igényel?

Az ilyen típusok olyan gyűjteménykifejezésekkel használhatók, amelyek explicit with() argumentumokat tartalmaznak, de a típusok nem használhatók paraméterekhez params .

Vegyük például a következő, gyári módszerből létrehozott típust:

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

Ugyanez a kérdés vonatkozik a konstruktor közvetlen meghívására, mint az alábbi példában.

Azoknál a céltípusoknál, ahol a konstruktort közvetlenül hívják meg, a gyűjteménykifejezés konvertálásához jelenleg argumentumok nélkül hívható konstruktorra van szükség, a gyűjtemény argumentumait azonban figyelmen kívül hagyja a rendszer az átalakíthatóság meghatározásakor.

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) { ... }
    // ...
}

Felbontás: Olyan céltípusokra való konvertálás támogatása, ahol minden konstruktor vagy gyári metódus argumentumokat igényel, és az átalakításhoz szükséges with() . LDM-2025-03-05

__arglist

Támogatni kell __arglist az elemeket with() ?

class MyCollection : IEnumerable
{
    public MyCollection(__arglist) { ... }
    public void Add(object o) { }
}

MyCollection c;
c = [with(__arglist())];    // ok
c = [with(__arglist(x, y)]; // ok

Felbontás: A gyűjtemény argumentumai csak __arglist akkor támogatottak, ha ingyenesek. LDM-2025-03-05

A felülettípusok argumentumai

Támogatni kell-e az argumentumokat a felületi céltípusok esetében?

ICollection<int> c = [with(capacity: 4)];
IReadOnlyDictionary<string, int> d = [with(comparer: StringComparer.Ordinal), ..values];
Ha igen, melyik metódus-aláírásokat használja az argumentumok kötésekor?

A mutable interface típusok esetében a lehetőségek a következők:

  1. Használja a példányosításhoz szükséges jól ismert típusból származó akadálymentes konstruktorokat: List<T> vagy Dictionary<K, V>.
  2. Használjon egyedi típustól független aláírásokat, például használjon new() és new(int capacity) használjon ICollection<T> és IList<T> használjon (lásd az egyes felületek lehetséges aláírásainak felépítését ).

Az akadálymentes konstruktorok jól ismert típusból való használata a következő következményekkel jár:

  • A paraméternevek (opcionális-ness) paramsközvetlenül a paraméterekből származnak.
  • Minden akadálymentes konstruktor megtalálható benne, annak ellenére, hogy ez nem feltétlenül hasznos a gyűjteménykifejezésekhez, például List(IEnumerable<T>) amelyek lehetővé tennék IList<int> list = [with(1, 2, 3)];.
  • A konstruktorok készlete a BCL-verziótól függhet.

Újraértelmezés: A jól ismert típusok akadálymentes konstruktorainak használata. Garantáltuk, hogy ezeket a típusokat használjuk, így ez csak "kiesik", és ez az értékek létrehozásának legtisztább és legegyszerűbb útja.

A nem mutable felülettípusok esetében a beállítások hasonlóak:

  1. Ne csinálj semmit. This
  2. Használjon adott típustól független aláírásokat, bár az egyetlen forgatókönyv a C#14 esetében lehet new(IEqualityComparer<K> comparer)IReadOnlyDictionary<K, V> .

Az akadálymentes konstruktorok használata valamilyen jól ismert típusból (a mutable-interface-types stratégiája) nem járható út, mivel nincs kapcsolat egyetlen meglévő típussal sem, és a végső típust használhatjuk és/vagy szintetizálhatjuk. Ezért furcsa új követelményeknek kell teljesülniük ahhoz, hogy a fordító képes legyen leképezni az említett típusú meglévő konstruktorokat (még a fejlődés során is) a ténylegesen létrehozott nem mutable példányra.

Újramegjelenítés: Adott típustól független aláírásokat használjon. A C# 14 esetében pedig csak a támogatás new(IEqualityComparer<K> comparer) , IReadOnlyDictionary<K, V> mivel ez az egyetlen nem használható felület, ahol úgy érezzük, hogy a használhatóság/szemantika szempontjából kritikus fontosságú, hogy a felhasználók ezt megadják. A jövőbeli C#-kiadások megfontolhatják a készlet kibővítését a megadott megalapozott indokok alapján.

Megoldás:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-04-23.md

A felületi céltípusok argumentumai támogatottak. A rendszer mind a mutable, mind a nem mutable interfészek esetében összeválogatja az argumentumokat.

A várt lista (amelyet továbbra is ldm-nek kell ratifikálni) az interfész céltípusa

Üres argumentumlisták

Engedélyeznünk kell üres argumentumlistákat néhány vagy az összes céltípushoz?

Az üres with() érték a nem with()értéknek felel meg. Lehet, hogy konzisztenciát biztosít a nem üres esetekhez, de nem ad hozzá új képességeket.

Az üres "with()" jelentése bizonyos céltípusok esetében egyértelműbb lehet, mint mások esetében: - Olyan típusok esetén, ahol **konstruktorokat** használ, argumentumok nélkül hívja meg az alkalmazható konstruktort. - A **'CollectionBuilderAttribute' **-val rendelkező típusok esetében csak elemeket tartalmazó gyári metódust kell meghívni. - **Interfésztípusok** esetén hozza létre a jól ismert vagy implementáció által definiált típust argumentumok nélkül. - A **tömbök** és a **spanok** esetében azonban, ahol a gyűjtemény argumentumai egyébként nem támogatottak, a "with()" zavaró lehet.
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?

Megoldás:https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-05-12.md#empty-argument-lists

Az argumentumok nélkül hívható konstruktortípusokhoz és építőtípusokhoz engedélyezzük a() lehetőséget, és üres konstruktor-aláírásokat adunk hozzá a felületi (módosítható és olvasható) típusokhoz. A tömbök és a spanok nem engedélyezik a() lehetőséget, mivel nincsenek olyan aláírások, amelyek elférnének rajtuk.

Kérdések megnyitása

Nyitott aggály véglegesítése a https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-03-17.md#conclusion

with(...)a nyelv kompatibilitástörő változása a következővel: .[with(...)] A funkció előtt egy egy elemet tartalmazó gyűjteménykifejezést jelent, amely a with-invocation-expression meghívásának eredménye. A funkció után ez egy gyűjtemény, amely argumentumokat ad át neki.

Azt szeretnénk, hogy ez a törés csak akkor történjen meg, ha egy felhasználó kiválaszt egy adott nyelvi verziót (például C#-14/15?). Más szóval, ha egy régebbi langversion-on vannak, a korábbi elemzési logikát kapják, de az újabb verzióban az újabb elemzési logikát kapják. A Vagy mindig azt akarjuk, hogy az újabb elemzési logika, még egy régebbi langversion?

Mindkét stratégiához van előzetes képünk. requiredpéldául a rendszer mindig elemzi az új logikát, a langversion-tól függetlenül. Míg mások record/field a nyelvi verziótól függően elemzik az elemzési logikát.

Végül ez átfedésben Dictionary Expressionsvan és hatással van a key:value KVP-elemek szintaxisára. Szeretnénk létrehozni a viselkedést szeretnénk minden lang verzió, és [with(...)] a saját, és a dolgok, mint [with(...) : expr] vagy [expr : with(...)].