Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Megjegyzés:
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/8697
Nyilatkozat
Szemantika
class_body
: '{' class_member_declaration* '}' ';'?
| ';'
;
class_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| finalizer_declaration
| static_constructor_declaration
| type_declaration
| extension_declaration // add
;
extension_declaration // add
: 'extension' type_parameter_list? '(' receiver_parameter ')' type_parameter_constraints_clause* extension_body
;
extension_body // add
: '{' extension_member_declaration* '}' ';'?
;
extension_member_declaration // add
: method_declaration
| property_declaration
| operator_declaration
;
receiver_parameter // add
: attributes? parameter_modifiers? type identifier?
;
A meghosszabbítási deklarációk csak nem általános, nem beágyazott statikus osztályokban deklarálhatók.
Hiba történt a névvel elnevezett extensiontípusnál.
Hatókörkezelési szabályok
A bővítménydeklaráció típusparaméterei és fogadóparaméterei a bővítménydeklaráció törzsében találhatók. Statikus tagon belül hibát jelent a vevőparaméterre hivatkozni, kivéve egy nameof kifejezésben. Hiba, hogy a tagok a típusparamétereket vagy paramétereket (valamint a helyi változókat és helyi függvényeket közvetlenül a tag törzsében) deklarálják ugyanazzal a névvel, mint a bővítménydeklaráció típusparamétere vagy fogadóparamétere.
public static class E
{
extension<T>(T[] ts)
{
public bool M1(T t) => ts.Contains(t); // `T` and `ts` are in scope
public static bool M2(T t) => ts.Contains(t); // Error: Cannot refer to `ts` from static context
public void M3(int T, string ts) { } // Error: Cannot reuse names `T` and `ts`
public void M4<T, ts>(string s) { } // Error: Cannot reuse names `T` and `ts`
}
}
Nem számít hibának, hogy a tagok neve megegyezik a közrezáró kiterjesztési deklaráció típusparaméterével vagy fogadóparaméterével. A tagnevek nem találhatók közvetlenül egy egyszerű névkeresésben a bővítménydeklarációban; A keresés így a tag helyett az adott név típusparaméterét vagy fogadóparaméterét fogja megtalálni.
A tagok azt eredményezik, hogy a statikus módszereket közvetlenül a belefoglaló statikus osztályba deklarálják, és ezek egyszerű névkereséssel találhatók meg; azonban először egy azonos nevű bővítménydeklarációs típusparaméter vagy fogadóparaméter található.
public static class E
{
extension<T>(T[] ts)
{
public void T() { M(ts); } // Generated static method M<T>(T[]) is found
public void M() { T(ts); } // Error: T is a type parameter
}
}
Statikus osztályok bővítménytárolóként
A bővítmények a legfelső szintű, nem általános statikus osztályokban vannak deklarálva, ugyanúgy, mint a bővítménymetelyek, és így együtt létezhetnek a klasszikus kiterjesztési módszerekkel és a nem kiterjesztésű statikus tagokkal:
public static class Enumerable
{
// New extension declaration
extension(IEnumerable source) { ... }
// Classic extension method
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
// Non-extension member
public static IEnumerable<int> Range(int start, int count) { ... }
}
Bővítménydeklarációk
A bővítménydeklaráció névtelen, és minden társított típusparamétert és korlátozást tartalmazó fogadó-specifikációt , majd a bővítménytag-deklarációk készletét tartalmazza. A vevő specifikációja lehet paraméter, vagy - ha csak statikus bővítménytagok deklarálva vannak - típus:
public static class Enumerable
{
extension(IEnumerable source) // extension members for IEnumerable
{
public bool IsEmpty { get { ... } }
}
extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
{
public IEnumerable<T> Where(Func<TSource, bool> predicate) { ... }
public IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) { ... }
}
extension<TElement>(IEnumerable<TElement>) // static extension members for IEnumerable<TElement>
where TElement : INumber<TElement>
{
public static IEnumerable<TElement> operator +(IEnumerable<TElement> first, IEnumerable<TElement> second) { ... }
}
}
A vevő specifikációjában szereplő típust fogadótípusnak nevezzük, és ha van ilyen, a paraméter nevét fogadóparaméternek nevezzük.
Ha a vevőparaméter neve el van nevezve, előfordulhat, hogy a fogadó típusa nem statikus.
A fogadóparaméter nem rendelkezhet módosítókkal, ha nincs megadva, és csak az alábbi és scoped egyéb listában szereplő refness módosítókat használhatja.
A fogadó paraméterre ugyanazok a korlátozások vonatkoznak, mint a klasszikus bővítménymetódus első paraméterére.
Az [EnumeratorCancellation] attribútum figyelmen kívül lesz hagyva, ha a fogadó paraméterre kerül.
Bővítménytagok
A bővítménytag-deklarációk szintaktikailag megegyeznek az osztály- és szerkezetdeklarációk megfelelő példányaival és statikus tagjaival (a konstruktorok kivételével). A példánytagok a fogadó objektumra a fogadó paraméter nevével hivatkoznak.
public static class Enumerable
{
extension(IEnumerable source)
{
// 'source' refers to receiver
public bool IsEmpty => !source.GetEnumerator().MoveNext();
}
}
Hiba egy példánybővítmény tagjának megadásakor, ha a bővítmény deklarációja nem ad meg fogadóparamétert:
public static class Enumerable
{
extension(IEnumerable) // No parameter name
{
public bool IsEmpty => true; // Error: instance extension member not allowed
}
}
Hiba a következő módosítók megadása egy bővítménydeklaráció egyik tagján: abstract, , virtual, overridenew, sealed, partialés (és protected a kapcsolódó akadálymentességi módosítók).
Hiba a módosító megadása egy readonly bővítménydeklaráció egy tagján.
A bővítménydeklarációk tulajdonságai nem feltétlenül rendelkeznek init tartozékokkal.
A példánytagok tiltva vannak, ha a fogadó paraméter nincs megnevezve.
Minden tagnak olyan neve van, amely eltér a statikus befoglaló osztály nevétől és a kiterjesztett típus nevétől, ha van ilyen.
Hiba egy bővítménytagot az attribútummal [ModuleInitializer] díszíteni.
Refness
Alapértelmezés szerint a fogadó érték szerint továbbítja a fogadót a példánybővítmény tagjainak, ugyanúgy, mint más paraméterek.
Azonban a paraméterként megadott bővítménydeklaráció fogadója képes megadni ref, ref readonly és in mindaddig, amíg a fogadó típusa ismert, mint értéktípus.
Nullkezelhetőség és attribútumok
A fogadótípusok lehetnek vagy tartalmazhatnak null értékű referenciatípusokat, és a paraméterek formájában megadott fogadó-specifikációk attribútumokat adhatnak meg:
public static class NullableExtensions
{
extension(string? text)
{
public string AsNotNull => text is null ? "" : text;
}
extension([NotNullWhen(false)] string? text)
{
public bool IsNullOrEmpty => text is null or [];
}
extension<T> ([NotNull] T t) where T : class?
{
public void ThrowIfNull() => ArgumentNullException.ThrowIfNull(t);
}
}
Kompatibilitás a klasszikus bővítménymetelyekkel
A példánykiterjesztési metódusok olyan összetevőket hoznak létre, amelyek megfelelnek a klasszikus bővítménymetelyek által előállítottaknak.
Pontosabban a létrehozott statikus metódus tartalmazza a deklarált bővítménymetódus attribútumait, módosítóit és nevét, valamint a típusparaméter-listát, a paraméterlistát és a kényszerlistát, amelyet a bővítménydeklarációból és a metódusdeklarációból fűz össze ebben a sorrendben:
public static class Enumerable
{
extension<TSource>(IEnumerable<TSource> source) // Generate compatible extension methods
{
public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
public IEnumerable<TSource> Select<TResult>(Func<TSource, TResult> selector) { ... }
}
}
Generál:
[Extension]
public static class Enumerable
{
[Extension]
public static IEnumerable<TSource> Where<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) { ... }
[Extension]
public static IEnumerable<TSource> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector) { ... }
}
Működtetők
Bár a bővítmény-operátorok explicit operandustípusokkal rendelkeznek, mégis deklarálni kell őket egy bővítménydeklarációban:
public static class Enumerable
{
extension<TElement>(IEnumerable<TElement>) where TElement : INumber<TElement>
{
public static IEnumerable<TElement> operator *(IEnumerable<TElement> vector, TElement scalar) { ... }
public static IEnumerable<TElement> operator *(TElement scalar, IEnumerable<TElement> vector) { ... }
}
}
Ez lehetővé teszi a típusparaméterek deklarálását és következtetését, és hasonló ahhoz, ahogyan a felhasználó által definiált operátorokat az egyik operandustípuson belül deklarálni kell.
Ellenőrzés
Következtetés: Minden nem metódusalapú bővítménytag esetében a bővítményblokk összes típusparaméterét a bővítmény és a tag paraméterkészletében kell használni.
Egyediség: Egy adott zárható statikus osztályon belül az azonos fogadótípusú bővítménytag-deklarációk (modulo identitásátalakítás és típusparaméternév helyettesítése) egyetlen deklarációs térként lesznek kezelve, hasonlóan az osztály vagy a szerkezet deklarációjának tagjaihoz, és ugyanazokra a szabályokra vonatkoznak az egyediségre vonatkozóan.
public static class MyExtensions
{
extension<T1>(IEnumerable<int>) // Error! T1 not inferrable
{
...
}
extension<T2>(IEnumerable<T2>)
{
public bool IsEmpty { get ... }
}
extension<T3>(IEnumerable<T3>?)
{
public bool IsEmpty { get ... } // Error! Duplicate declaration
}
}
Ennek az egyediségi szabálynak az alkalmazása magában foglalja az ugyanazon statikus osztályban található klasszikus kiterjesztési módszereket.
A bővítménydeklarációk metódusaival való összehasonlítás céljából a this paraméter fogadó-specifikációként, az adott fogadótípusban említett típusparaméterekkel együtt, a többi típusparamétert és metódusparamétert pedig a metódus-aláíráshoz használja:
public static class Enumerable
{
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
extension(IEnumerable source)
{
IEnumerable<TResult> Cast<TResult>() { ... } // Error! Duplicate declaration
}
}
Fogyasztás
A bővítménytagok keresésének megkísérlésekor az importált statikus osztályokban usinglévő összes bővítménydeklaráció jelöltként járul hozzá a tagokhoz, a fogadó típusától függetlenül. Csak a megoldás részeként vannak elvetve a nem kompatibilis fogadótípusokkal rendelkező jelöltek.
Az argumentumok típusa (beleértve a tényleges fogadót is) és a típusparaméterek (kombinálva a bővítmény deklarációjában és a bővítménytag deklarációjában szereplőkkel) között teljes általános típusinferenciát kísérelünk meg.
Explicit típusargumentumok megadásakor a bővítmény deklarációjának és a bővítménytag-deklaráció típusparamétereinek helyettesítésére szolgálnak.
string[] strings = ...;
var query = strings.Select(s => s.Length); // extension invocation
var query2 = strings.Select<string, int>(s => s.Length); // ... with explicit full set of type arguments
var query3 = Enumerable.Select(strings, s => s.Length); // static method invocation
var query4 = Enumerable.Where<string, int>(strings, s => s.Length); // ... with explicit full set of type arguments
public static class Enumerable
{
extension<TSource>(IEnumerable<TSource> source)
{
public IEnumerable<TResult> Select<TResult>(Func<T, TResult> predicate) { ... }
}
}
A klasszikus bővítménymetelyekhez hasonlóan a kibocsátott implementációs metódusok statikusan is meghívhatók.
Ez lehetővé teszi a fordító számára az azonos nevű és aritású bővítménytagok egyértelműsítését.
object.M(); // ambiguous
E1.M();
new object().M2(); // ambiguous
E1.M2(new object());
_ = _new object().P; // ambiguous
_ = E1.get_P(new object());
static class E1
{
extension(object)
{
public static void M() { }
public void M2() { }
public int P => 42;
}
}
static class E2
{
extension(object)
{
public static void M() { }
public void M2() { }
public int P => 42;
}
}
A statikus bővítménymetódusokat a példánykiterjesztési metódusokhoz hasonlóan feloldjuk (a fogadó típusának további argumentumát fogjuk figyelembe venni).
A bővítménytulajdonságok a bővítménymetódushoz hasonlóan egy paraméterrel (a fogadó paraméterrel) és egyetlen argumentummal (a tényleges fogadóértékkel) lesznek feloldva.
using static Irányelvek
Egy using_static_directive elérhetővé teszi a típusdeklaráció bővítményblokkjainak tagjait a bővítményhozzáféréshez.
using static N.E;
new object().M();
object.M2();
_ = new object().Property;
_ = object.Property2;
C c = null;
_ = c + c;
c += 1;
namespace N
{
static class E
{
extension(object o)
{
public void M() { }
public static void M2() { }
public int Property => 0;
public static int Property2 => 0;
}
extension(C c)
{
public static C operator +(C c1, C c2) => throw null;
public void operator +=(int i) => throw null;
}
}
}
class C { }
A korábbiakhoz hasonlóan az adott típus deklarációjában közvetlenül szereplő akadálymentes statikus tagokra (a kiterjesztési módszerek kivételével) közvetlenül lehet hivatkozni.
Ez azt jelenti, hogy a megvalósítási módszerek (a bővítménymetelyek kivételével) közvetlenül statikus metódusként használhatók:
using static E;
M();
System.Console.Write(get_P());
set_P(43);
_ = op_Addition(0, 0);
_ = new object() + new object();
static class E
{
extension(object)
{
public static void M() { }
public static int P { get => 42; set { } }
public static object operator +(object o1, object o2) { return o1; }
}
}
A using_static_directive még mindig nem importálja közvetlenül statikus metódusként a bővítménymetódusokat, így a nem statikus bővítménymetódusok implementálási metódusa nem hívható meg közvetlenül statikus metódusként.
using static E;
M(1); // error: The name 'M' does not exist in the current context
static class E
{
extension(int i)
{
public void M() { }
}
}
OverloadResolutionPriorityAttribute
A melléktagok egy zárható statikus osztályon belül az ORPA-értékek alapján rangsorolásnak vannak kitéve. A beágyazó statikus osztályt az ORPA-szabályok által figyelembe vett "tartalmazó típusnak" tekintjük.
A bővítménytulajdonságokon található ORPA-attribútumok át lesznek másolva a tulajdonság tartozékainak implementálási módszereire, így a rendszer tiszteletben tartja a rangsorolást, amikor a bővítményeket egyértelműsítési szintaxissal használják.
Belépési pontok
A bővítményblokkok módszerei nem minősülnek belépési pontnak (lásd: "7.1 Alkalmazás indítása"). Megjegyzés: a megvalósítási módszer továbbra is lehetséges jelölt lehet.
Csökkentés
A bővítménydeklarációk csökkentő stratégiája nem nyelvi szintű döntés. A nyelvi szemantikán túl azonban bizonyos követelményeknek is meg kell felelnie:
- A létrehozott típusok, tagok és metaadatok formátumát minden esetben egyértelműen meg kell adni, hogy más fordítók is felhasználhassák és létrehozhassák azt.
- A létrehozott összetevőknek stabilnak kell lenniük, abban az értelemben, hogy az ésszerű későbbi módosítások nem akadályozhatják meg a korábbi verziókhoz összeállított felhasználókat.
Ezeknek a követelményeknek a megvalósítás előrehaladása során további finomítást kell igényelniük, és előfordulhat, hogy szélsőséges esetekben kompromisszumot kell kötni az ésszerű megvalósítási megközelítés érdekében.
Deklarációk metaadatai
Célok
Az alábbi kialakítás a következőket teszi lehetővé:
- a bővítménydeklaráció szimbólumainak lekerekítése metaadatokon keresztül (teljes és referenciaszerelvények),
- a bővítménytagokra mutató stabil hivatkozások (xml-dokumentumok),
- a kibocsátott nevek helyi meghatározása (az EnC esetében hasznos),
- nyilvános API-nyomkövetés.
Xml-dokumentumok esetén a bővítménytag docID azonosítója a metaadatok bővítménytagjának docID azonosítója. A cref="Extension.extension(object).M(int)" használt docID például az M:Extension.<>E__ExtensionGroupingTypeNameForObject.M(System.Int32), és ez a docID stabil marad a bővítményblokkok újrafordítása és újrarendezése során. Ideális esetben az is stabil marad, ha a bővítményblokkok korlátozásai megváltoznak, de nem találtunk olyan tervet, amely ezt a tagütközések nyelvi kialakítására gyakorolt káros hatás nélkül érné el.
Az EnC esetében hasznos helyben tudni (csak egy módosított kiterjesztés tagot nézve), hogy a frissített kiterjesztés tag hol kerül kibocsátásra a metaadatokban.
A nyilvános API-nyomkövetéshez a stabilabb nevek csökkentik a zajt. Az ilyen helyzetekben azonban a bővítménycsoportozási típusneveknek gyakorlatilag nem szabad szerepet játszaniuk. Ha a bővítmény tagját Mnézzük, nem számít, hogy mi a bővítménycsoportozási típus neve, ami számít, az annak a bővítményblokknak az aláírása, amelyhez tartozik. A nyilvános API-aláírást nem szabad úgy tekinteni, mint Extension.<>E__ExtensionGroupingTypeNameForObject.M(System.Int32) inkább .Extension.extension(object).M(int) Más szóval a bővítménytagoknak két típusparaméter-készlettel és két paraméterkészlettel kell rendelkeznie.
Áttekintés
A bővítményblokkok a CLR-szintű aláírásuk szerint vannak csoportosítva. A rendszer minden CLR-egyenértékűségi csoportot tartalomalapú névvel rendelkező bővítménycsoport-típusként bocsát ki. A CLR-ekvivalenciacsoporton belüli bővítményblokkokat ezután a C# egyenértékűség alcsoportba sorolja. Minden C# egyenértékűségi csoport egy tartalomalapú névvel rendelkező bővítményjelölőtípusként lesz kibocsátva, a megfelelő bővítménycsoportozási típusba ágyazva. A bővítményjelölők típusa egyetlen bővítményjelölőmetódust tartalmaz, amely egy bővítményparamétert kódol. A bővítményjelölő módszer a hozzá tartozó típusával teljes hűséggel kódolja a bővítményblokk aláírását. Az egyes bővítménytagok deklarálása a megfelelő bővítménycsoportozási típusban történik, egy attribútumon keresztül hivatkozik vissza egy bővítményjelölő típusára, és egy legfelső szintű statikus megvalósítási módszer kíséri, módosított aláírással.
A metaadatok kódolásának sémaalapú áttekintése:
[Extension]
static class EnclosingStaticClass
{
[Extension]
public sealed class ExtensionGroupingType1 // has type parameters with minimal constraints sufficient to keep extension member declarations below valid
{
public static class ExtensionMarkerType1 // has re-declared type parameters with full fidelity of C# constraints
{
public static void <Extension>$(... extension parameter ...) // extension marker method
}
... ExtensionMarkerType2, etc ...
... extension members for ExtensionGroupingType1, each points to its corresponding extension marker type ...
}
... ExtensionGroupingType2, etc ...
... implementation methods ...
}
A beágyazó statikus osztály egy [Extension] attribútummal lesz kibocsátva.
CLR-szintű aláírás és C#-szintű aláírás
A bővítményblokk CLR-szintű aláírása a következőből származik:
- a típusparaméterek neveinek normalizálása
T0,T1stb. - attribútumok eltávolítása
- a paraméternév törlése
- paramétermódosítók törlése (például
ref: ,in,scoped...) - tuple nevek törlése
- érvénytelenségi széljegyzetek törlése
-
notnullkorlátozások törlése
Megjegyzés: a rendszer megőrzi az egyéb kényszereket, például new(), struct, class, allows ref struct, unmanaged és a típuskényszereket.
Bővítménycsoportozási típusok
A rendszer egy bővítménycsoportozási típust bocsát ki a metaadatok számára a forrásban található, azonos CLR-szintű aláírással rendelkező bővítményblokk-csoportok mindegyikéhez.
- Neve kimondhatatlan, és a CLR-szintű aláírás tartalma alapján határozható meg. További részletek alább.
- A típusparaméterek normalizált névvel (
T0, , ...) rendelkeznek,T1és nem rendelkeznek attribútumokkal. - Nyilvános és lezárt.
- A
specialnamejelzővel és egy[Extension]attribútummal van megjelölve.
A bővítménycsoportozási típus tartalomalapú neve a CLR-szintű aláíráson alapul, és a következőket tartalmazza:
- A bővítményparaméter típusának teljes clR-neve.
- A hivatkozott típusparaméterek neve normalizálva lesz a következőre
T0: ,T1stb. a típusdeklarációban megjelenő sorrend alapján. - A teljes név nem tartalmazza az azt tartalmazó szerelvényt. Gyakran előfordul, hogy a típusokat a szerelvények között kell áthelyezni, és ez nem szakíthatja meg az XML dokumentum hivatkozásait.
- A hivatkozott típusparaméterek neve normalizálva lesz a következőre
- A típusparaméterek korlátozásai bele lesznek foglalva és rendezve lesznek, így az újrarendezésük a forráskódban nem változtatja meg a nevet. Kifejezetten:
- A típusparaméter-megkötések deklarációs sorrendben jelennek meg. Az Nth+1 típusparaméter előtt az Nth+1 típusú paraméterre vonatkozó korlátozások lépnek fel.
- A típuskorlátozások rendezése a teljes nevek ordinális összehasonlításával lesz.
- A nem típusmegkötések determinisztikus sorrendben vannak rendezve, és úgy vannak kezelve, hogy elkerülhető legyen a kétértelműség vagy a típuskorlátozásokkal való ütközés.
- Mivel ez nem tartalmaz attribútumokat, szándékosan figyelmen kívül hagyja a C#-specifikus elemeket, mint például a tuple nevek, a nullabilitás stb.
Megjegyzés: A név garantáltan stabil marad az újrafordítások, az újrarendezések és a C# jellemzők olyan módosításai esetén, amelyek nem befolyásolják a CLR-szintű aláírást.
Bővítményjelölők típusai
A jelölőtípus újra deklarálja az azt tartalmazó csoportosítási típus (bővítménycsoportozási típus) típusparamétereit, hogy a bővítményblokkok C#-nézetének teljes megbízhatóságát megkapja.
A rendszer egy kiterjesztésjelölőtípust bocsát ki a metaadatok számára a forrásban lévő, azonos C#-szintű aláírással rendelkező bővítményblokkok minden egyes halmazához.
- Neve kimondhatatlan, és a bővítményblokk C#-szintű aláírásának tartalma alapján határozható meg. További részletek alább.
- A forrásban deklarált típusú csoportosítási típus típusparamétereit (beleértve a nevet és az attribútumokat) ismételten bejelenti.
- Nyilvános és statikus.
- A jelölővel
specialnamevan megjelölve.
A bővítményjelölő típusának tartalomalapú neve az alábbiakon alapul:
- A típusparaméterek neve szerepel a bővítménydeklarációban megjelenő sorrendben
- A típusparaméterek attribútumai úgy lesznek belefoglalva és rendezve, hogy a forráskódban való átrendezés nem változtatja meg a nevet.
- A típusparaméterek korlátozásai bele lesznek foglalva és rendezve lesznek, így az újrarendezésük a forráskódban nem változtatja meg a nevet.
- A kiterjesztett típus teljes C#-neve
- Ez olyan elemeket fog tartalmazni, mint a nullálható annotációk, a tupel nevek stb.
- A teljes név nem tartalmazza az azt tartalmazó szerelvényt
- A bővítményparaméter neve
- A bővítményparaméter (
ref, ,ref readonly,scoped...) módosítói determinisztikus sorrendben - A bővítményparaméterre alkalmazott attribútumok teljes név- és attribútumargumentumai determinisztikus sorrendben
Megjegyzés: A név garantáltan stabil marad az újrafordítás és az újrarendezés során.
Megjegyzés: A bővítményjelölő típusok és a bővítményjelölő metódusok a referenciaszerelvények részeként kerülnek kibocsátásra.
Bővítményjelző módszer
A jelölőmetódus célja a bővítményblokk bővítményparaméterének kódolása. Mivel tagja a bővítményjelölő típusának, hivatkozhat a bővítményjelölő típusának újra deklarált típusparaméterére.
Minden kiterjesztésjelölőtípus egyetlen metódust, a bővítményjelölő metódust tartalmazza.
- Ez statikus, nem általános, void visszatérésű, és az úgynevezett
<Extension>$. - Egyetlen paramétere a bővítményparaméter attribútumaival, refness-ével, típusával és nevével rendelkezik.
Ha a bővítményparaméter nem ad meg nevet, akkor a paraméter neve üres. - A jelölővel
specialnamevan megjelölve.
A jelölőmetódus akadálymentessége a legkevésbé korlátozó akadálymentesség lesz a megfelelő deklarált bővítménytagok között; amennyiben nincs ilyen deklarálva, private értelmében lesz alkalmazva.
Bővítménytagok
A forrásban lévő bővítményblokk metódus-/tulajdonságdeklarációi a metaadatok bővítménycsoportozási típusának tagjaiként jelennek meg.
- Az eredeti módszerek szignatúrái megmaradnak (az attribútumokat is beleértve), de a testüket lecserélik
throw NotImplementedException(). - Ezekre az il-ben nem szabad hivatkozni.
- A metódusok, tulajdonságok és tartozékaik a tag bővítményblokkjának megfelelő kiterjesztésjelölőtípus nevére hivatkozva vannak megjelölve
[ExtensionMarkerName("...")].
Implementálási módszerek
A forrásban lévő bővítményblokk metódus-/tulajdonságdeklarációinak metódustestei statikus implementációs metódusként jelennek meg a legfelső szintű statikus osztályban.
- A megvalósítási metódus neve megegyezik az eredeti metódus nevével.
- A bővítményblokkból származó típusparaméterek az eredeti metódus típusparamétereihez vannak előállítva (az attribútumokat is beleértve).
- Ugyanazokkal az akadálymentességgel és attribútumokkal rendelkezik, mint az eredeti módszer.
- Ha statikus metódust implementál, ugyanazokkal a paraméterekkel és visszatérési típussal rendelkezik.
- Ha implementál egy példánymetódust, egy előre felfűzött paraméterrel rendelkezik az eredeti metódus aláírásához. Ennek a paraméternek az attribútumai, a refness, a típus és a név a megfelelő bővítményblokkban deklarált bővítményparaméterből származik.
- Az implementálási metódusok paraméterei a implementálási módszer által birtokolt típusparaméterekre vonatkoznak a bővítményblokkok helyett.
- Ha az eredeti metódus egy példány metódus, akkor a megvalósítási metódus
[Extension]attribútummal van megjelölve.
ExtensionMarkerName attribútum
A ExtensionMarkerNameAttribute típus csak fordítóhoz használható – a forrásban nem engedélyezett. A típusdeklarációt a fordító szintetizálja, ha még nem szerepel a fordításban.
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
public sealed class ExtensionMarkerNameAttribute : Attribute
{
public ExtensionMarkerNameAttribute(string name)
=> Name = name;
public string Name { get; }
}
Megjegyzés: Bár néhány attribútumcél szerepel a jövőbiztosításhoz (bővítmények beágyazott típusai, bővítménymezők, bővítményesemények), az AttributeTargets.Konstruktor nem szerepel benne, mivel a bővítménykonstruktorok nem konstruktorok.
példa
Megjegyzés: egyszerűsített tartalomalapú neveket használunk a példához az olvashatóság érdekében. Megjegyzés: mivel a C# nem képes a típusparaméter újradeklarálására, a metaadatokat képviselő kód érvénytelen C# kód.
Íme egy példa, amely bemutatja, hogyan működik a csoportosítás tagok nélkül:
class E
{
extension<T>(IEnumerable<T> source)
{
... member in extension<T>(IEnumerable<T> source)
}
extension<U>(ref IEnumerable<U?> p)
{
... member in extension<U>(ref IEnumerable<U?> p)
}
extension<T>(IEnumerable<U> source)
where T : IEquatable<U>
{
... member in extension<T>(IEnumerable<U> source) where T : IEquatable<U>
}
}
kibocsátva:
[Extension]
class E
{
[Extension, SpecialName]
public sealed class <>E__ContentName_For_IEnumerable_T<T0>
{
[SpecialName]
public static class <>E__ContentName1 // note: re-declares type parameter T0 as T
{
[SpecialName]
public static void <Extension>$(IEnumerable<T> source) { }
}
[SpecialName]
public static class <>E__ContentName2 // note: re-declares type parameter T0 as U
{
[SpecialName]
public static void <Extension>$(ref IEnumerable<U?> p) { }
}
[ExtensionMarkerName("<>E__ContentName1")]
... member in extension<T>(IEnumerable<T> source)
[ExtensionMarkerName("<>E__ContentName2")]
... member in extension<U>(ref IEnumerable<U?> p)
}
[Extension, SpecialName]
public sealed class <>ContentName_For_IEnumerable_T_With_Constraint<T0>
where T0 : IEquatable<T0>
{
[SpecialName]
public static class <>E__ContentName3 // note: re-declares type parameter T0 as U
{
[SpecialName]
public static void <Extension>$(IEnumerable<U> source) { }
}
[ExtensionMarkerName("ContentName3")]
public static bool IsPresent(U value) => throw null!;
}
... implementation methods
}
Íme egy példa a tagok kibocsátásának módjára:
static class IEnumerableExtensions
{
extension<T>(IEnumerable<T> source) where T : notnull
{
public void Method() { ... }
internal static int Property { get => ...; set => ...; }
public int Property2 { get => ...; set => ...; }
}
extension(IAsyncEnumerable<int> values)
{
public async Task<int> SumAsync() { ... }
}
public static void Method2() { ... }
}
kibocsátva:
[Extension]
static class IEnumerableExtensions
{
[Extension, SpecialName]
public sealed class <>E__ContentName_For_IEnumerable_T<T0>
{
// Extension marker type is emitted as a nested type and re-declares its type parameters to include C#-isms
// In this example, the type parameter `T0` is re-declared as `T` with a `notnull` constraint:
// .class <>E__IEnumerableOfT<T>.<>E__ContentName_For_IEnumerable_T_Source
// .typeparam T
// .custom instance void NullableAttribute::.ctor(uint8) = (...)
[SpecialName]
public static class <>E__ContentName_For_IEnumerable_T_Source
{
[SpecialName]
public static <Extension>$(IEnumerable<T> source) => throw null;
}
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
public void Method() => throw null;
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
internal static int Property
{
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
get => throw null;
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
set => throw null;
}
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
public int Property2
{
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
get => throw null;
[ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
set => throw null;
}
}
[Extension, SpecialName]
public sealed class <>E__ContentName_For_IAsyncEnumerable_Int
{
[SpecialName]
public static class <>E__ContentName_For_IAsyncEnumerable_Int_Values
{
[SpecialName]
public static <Extension>$(IAsyncEnumerable<int> values) => throw null;
}
[ExtensionMarkerName("<>E__ContentName_For_IAsyncEnumerable_Int_Values")]
public Task<int> SumAsync() => throw null;
}
// Implementation for Method
[Extension]
public static void Method<T>(IEnumerable<T> source) { ... }
// Implementation for Property
internal static int get_Property<T>() { ... }
internal static void set_Property<T>(int value) { ... }
// Implementation for Property2
public static int get_Property2<T>(IEnumerable<T> source) { ... }
public static void set_Property2<T>(IEnumerable<T> source, int value) { ... }
// Implementation for SumAsync
[Extension]
public static int SumAsync(IAsyncEnumerable<int> values) { ... }
public static void Method2() { ... }
}
Amikor bővítménytagokat használnak a forrásban, ezeket a végrehajtási módszerekre hivatkozva bocsátjuk ki.
Például: egy enumerableOfInt.Method() hívása statikus hívásként lenne kibocsátva IEnumerableExtensions.Method<int>(enumerableOfInt).
XML-dokumentumok
A bővítményblokkra vonatkozó doc-megjegyzések a jelölőtípushoz lesznek kibocsátva (a bővítményblokk DocID azonosítója az alábbi példában található E.<>E__MarkerContentName_For_ExtensionOfT'1 ).
Hivatkozhatnak a bővítményparaméterre és a típusparaméterekre a <paramref> és <typeparamref> használatával.
Megjegyzés: Nem dokumentálhatja a bővítményparamétert vagy a típusparamétereket (<param> és <typeparam> használatával) egy bővítménytagon.
Ha két bővítményblokkot bocsát ki egy jelölőtípusként, a dokumentum megjegyzései is egyesülnek.
Az xml-dokumentumokat kezelő eszközök feladata, hogy a <param> és <typeparam> elemeket a kiterjesztési blokkból a megfelelő kiterjesztési tagokra másolják, azaz a paraméterinformációkat csak a példánytagokra kell másolni.
Kibocsátanak egy <inheritdoc> a végrehajtási módszerekre vonatkozóan, és ez cref segítségével hivatkozik a megfelelő bővítménytagra. A getter implementálási módszere például a bővítménytulajdonság dokumentációjára hivatkozik.
Ha a bővítménytag nem rendelkezik dokumentum megjegyzésekkel, akkor a függvény nem adja meg a <inheritdoc> megjegyzést.
A bővítményblokkok és a bővítménytagok esetében jelenleg nem figyelmeztetjük, ha:
- a bővítményparaméter dokumentálva van, de a bővítménytag paraméterei nem
- vagy fordítva
- vagy a nem dokumentált típusparaméterekkel rendelkező egyenértékű forgatókönyvekben
Például a következő dokumentum megjegyzései:
/// <summary>Summary for E</summary>
static class E
{
/// <summary>Summary for extension block</summary>
/// <typeparam name="T">Description for T</typeparam>
/// <param name="t">Description for t</param>
extension<T>(T t)
{
/// <summary>Summary for M, which may refer to <paramref name="t"/> and <typeparamref name="T"/></summary>
/// <typeparam name="U">Description for U</typeparam>
/// <param name="u">Description for u</param>
public void M<U>(U u) => throw null!;
/// <summary>Summary for P</summary>
public int P => 0;
}
}
a következő xml-t adja meg:
<?xml version="1.0"?>
<doc>
<assembly>
<name>Test</name>
</assembly>
<members>
<member name="T:E">
<summary>Summary for E</summary>
</member>
<member name="T:E.<>E__MarkerContentName_For_ExtensionOfT`1">
<summary>Summary for extension block</summary>
<typeparam name="T">Description for T</typeparam>
<param name="t">Description for t</param>
</member>
<member name="M:E.<>E__MarkerContentName_For_ExtensionOfT`1.M``1(``0)">
<summary>Summary for M, which may refer to <paramref name="t"/> and <typeparamref name="T"/></summary>
<typeparam name="U">Description for U</typeparam>
<param name="u">Description for u</param>
</member>
<member name="P:E.<>E__MarkerContentName_For_ExtensionOfT`1.P">
<summary>Summary for P</summary>
</member>
<member name="M:E.M``2(``0,``1)">
<inheritdoc cref="M:E.<>E__MarkerContentName_For_ExtensionOfT`1.M``1(``0)"/>
</member>
<member name="M:E.get_P``1(``0)">
<inheritdoc cref="P:E.<>E__MarkerContentName_For_ExtensionOfT`1.P"/>
</member>
</members>
</doc>
CREF-hivatkozások
A bővítményblokkokat úgy kezelhetjük, mint a beágyazott típusokat, amelyek az aláírásukkal kezelhetők (mintha egyetlen bővítményparaméterrel rendelkező metódus lenne).
Példa: E.extension(ref int).M().
A cref azonban nem tudja kezelni magát a bővítményblokkot.
E.extension(int) egy "extension" nevű metódusra hivatkozhat a típusban E.
static class E
{
extension(ref int i)
{
void M() { } // can be addressed by cref="E.extension(ref int).M()" or cref="extension(ref int).M()" within E, but not cref="M()"
}
extension(ref int i)
{
void M(int i2) { } // can be addressed by cref="E.extension(ref int).M(int)" or cref="extension(ref int).M(int)" within E
}
}
A keresés úgy van beállítva, hogy az összes megfelelő kiterjesztési blokkban keres.
Mivel nem engedélyezzük a bővítménytagokra való nem minősített hivatkozásokat, a cref ezeket is letiltaná.
A szintaxis a következő:
member_cref
: conversion_operator_member_cref
| extension_member_cref // added
| indexer_member_cref
| name_member_cref
| operator_member_cref
;
extension_member_cref // added
: 'extension' type_argument_list? cref_parameter_list '.' member_cref
;
qualified_cref
: type '.' member_cref
;
cref
: member_cref
| qualified_cref
| type_cref
;
Hiba a extension_member_cref használata a legfelső szinten (extension(int).M) vagy egy másik bővítménybe (E.extension(int).extension(string).M) ágyazva.
Kritikus változások
Típusok és aliasok nem nevezhetők "extension"-nek.
Nyitott problémák
A dokumentum nyitott problémákkal kapcsolatos ideiglenes szakasza, beleértve a nem definiált szintaxis és az alternatív tervek megvitatását
- Módosítani kell a fogadó összetevő követelményeit egy bővítmény elem elérésekor? (megjegyzés)
-
Erősítse meg(válasz:extensionvs.extensionskulcsszókéntextension, LDM 2025-03-24) -
Győződjön meg arról, hogy le szeretnénk tiltani(válasz: igen, letiltás, LDM 2025-06-11)[ModuleInitializer] -
Győződjön meg arról, hogy elvethetjük-e a bővítményblokkokat mint belépési pont jelölteket(válasz: igen, elvetés, LDM 2025-06-11) -
Erősítse meg a LangVer-logikát (hagyja ki az új bővítményeket, és vegye figyelembe és jelentse őket, ha kiválasztják)(válasz: feltétel nélküli kötés és LangVer-hiba jelentése a példánykiterjesztési módszerek kivételével, LDM 2025-06-11) Kötelező legyen a(válasz: a dokumentum megjegyzései csendesen egyesülnek, amikor a blokkok egyesülnek, nincspartialaz olyan bővítményblokkokhoz, amelyek egyesülnek, és össze kell vonni a dokumentummegjegyzéseiket?partialszükség, 2025-09-03 e-mailben megerősítve)Győződjön meg arról, hogy a tagokat nincs szükség az őket magában foglaló vagy kiterjesztett típusok szerint elnevezni.(válasz: igen, megerősítve e-mailben 2025-09-03)
A csoportosítási/ütközési szabályok újbóli kezelése a hordozhatósági probléma fényében: https://github.com/dotnet/roslyn/issues/79043
(válasz: ez a forgatókönyv a tartalomalapú típusnevekkel rendelkező új metaadat-tervezés részeként lett megoldva, engedélyezett)
A jelenlegi logika az azonos fogadótípusú bővítményblokkok csoportosítása. Ez nem veszi figyelembe a korlátozásokat. Ez hordozhatósági problémát okoz ezzel a forgatókönyvvel:
static class E
{
extension<T>(ref T) where T : struct
void M()
extension<T>(T) where T : class
void M()
}
A javaslat ugyanazt a csoportosítási logikát használja, amelyet a bővítménycsoportozás típusának tervezéséhez tervezünk, nevezetesen a CLR-szintű megkötések figyelembe vételéhez (azaz figyelmen kívül hagyva a notnullt, a rekordneveket, a nullitási megjegyzéseket).
Kódolni kell a "refness"-t a csoportosítási típus nevében?
-
Tekintse át azt a javaslatot, amely(válasz: e-mailben megerősítve: 2025-09-03)refnem szerepel a bővítménycsoport-típusnévben (a WG újra megvizsgálja a csoportosítási/ütközési szabályokat, LDM 2025-06-23)
public static class E
{
extension(ref int)
{
public static void M()
}
}
A rendszer a következő módon bocsátja ki:
public static class E
{
public static class <>ExtensionTypeXYZ
{
.. marker method ...
void M()
}
}
A harmadik fél által készített CREF-referenciák E.extension(ref int).M úgy lesznek kibocsátva, mint M:E.<>ExtensionGroupingTypeXYZ.M(), ha ref-t eltávolítanak vagy hozzáadnak egy bővítményparaméterhez, valószínűleg nem szeretnénk, hogy a CREF megszakadjon.
Nem nagyon foglalkozunk ezzel a forgatókönyvvel, mivel a bővítményként való használat kétértelmű lenne:
public static class E
{
extension(ref int)
static void M()
extension(int)
static void M()
}
Ez a forgatókönyv azonban fontos (a hordozhatóság és a hasznosság szempontjából), és az ütközési szabályok módosítása után ez a javasolt metaadat-kialakításnak is működnie kell:
static class E
{
extension<T>(ref T) where T : struct
void M()
extension<T>(T) where T : class
void M()
}
A refness figyelembevételének elhagyása hátrányos, mert elveszítjük a hordozhatóságot ebben a forgatókönyvben.
static class E
{
extension<T>(ref T)
void M()
extension<T>(T)
void M()
}
// portability issue: since we're grouping without accounting for refness, the emitted extension members conflict (not implementation members). Mitigation: keep as classic extensions or split to another static class
név szerinti
Tiltani kéne a kiterjesztéstulajdonságokat a nameof-ban, mint ahogy tesszük a klasszikus és az új bővítménymetódusokkal?(válasz: a "nameof(EnclosingStaticClass.ExtensionMember) nevet szeretnénk használni. Tervezésre van szükség, valószínűleg a .NET 10-ből. LDM 2025-06-11)
mintaalapú szerkezetek
Módszerek
Hol érdemes új bővítménymetelyeket bevetni?(válasz: ugyanazok a helyek, ahol a klasszikus bővítménymetszetek játszhatók le, LDM 2025-05-05)
Ez a következőket foglalja magában:
-
GetEnumerator/GetAsyncEnumeratorbanforeach -
Deconstructa dekonstruálásban, a pozíciómintában és a foreach ciklusban -
Adda gyűjtemény inicializálóiban -
GetPinnableReferencea(z)fixedrendszerben -
GetAwaitera(z)awaitrendszerben
Ez kizárja a következőt:
-
Dispose/DisposeAsyncusingésforeach -
MoveNext/MoveNextAsyncbanforeach -
Sliceésintindexelők implicit indexelőkben (és esetleg listamintákban?) -
GetResulta(z)awaitrendszerben
Tulajdonságok és indexelők
Hol érdemes a bővítménytulajdonságokat és indexelőket használni?(válasz: kezdjük a néggyel, LDM 2025-05-05)
A következőket tartalmaznánk:
- objektum-inicializáló:
new C() { ExtensionProperty = ... } - szótár inicializáló:
new C() { [0] = ... } -
with:x with { ExtensionProperty = ... } - tulajdonságminták:
x is { ExtensionProperty: ... }
Kizárjuk a következőt:
-
Currenta(z)foreachrendszerben -
IsCompleteda(z)awaitrendszerben -
Count/Lengthtulajdonságok és indexelők a listamintában -
Count/Lengthtulajdonságok és indexelők implicit indexelőkben
Visszatérő delegáltakat adó tulajdonságok
Győződjön meg arról, hogy az alakzat bővítménytulajdonságai csak a LINQ-lekérdezésekben kerülnek alkalmazásra, hogy megfeleljenek a példánytulajdonságoknak.(válasz: van értelme, LDM 2025-04-06)
Lista- és terjedési minta
- Győződjön meg arról, hogy a bővítményindexelőknek
Index/Rangelistamintákban kell játszaniuk (válasz: a C# 14 esetében nem releváns)
Tekintsük át, hogy a bővítménytulajdonságok hogyan kerülnek szerepbe Count/Length
Gyűjteménykifejezések
- A bővítmény
Addműködik - A bővítmény
GetEnumeratorterjesztéshez működik - A bővítmény
GetEnumeratornem befolyásolja az elemtípus meghatározását (példánynak kell lennie) - A statikus
Createbővítménymetódusok nem számítanak áldott létrehozási módszernek - Hatással vannak-e a bővítmények megszámlálható tulajdonságai a gyűjteménykifejezésekre?
params gyűjtemények
- A kiegészítők
Addnem befolyásolják, hogy milyen típusok engedélyezettekparams
szótárkifejezések
- Győződjön meg arról, hogy a bővítményindexelők nem játszanak a szótárkifejezésekben, mivel az indexelő jelenléte szerves része annak, ami meghatározza a szótártípust. (válasz: c# 14 esetén nem releváns)
extern
- a
hordozhatóság engedélyezését(válasz: jóváhagyva, LDM 2025-06-23)externtervezzük: https://github.com/dotnet/roslyn/issues/78572
Elnevezési/számozási séma bővítménytípushoz
Probléma
A jelenlegi számozási rendszer problémákat okoz a nyilvános API-k ellenőrzésével kapcsolatban, ami biztosítja, hogy a nyilvános API-k egyezzenek a csak referencia-szerelvények és a megvalósítási szerelvények között.
Meg kellene tennünk az alábbi módosítások egyikét? (válasz: tartalomalapú elnevezési sémát vezetünk be a nyilvános API stabilitásának növelése érdekében, és az eszközöket továbbra is frissíteni kell a jelölőmódszerek figyelembe vételéhez)
- az eszköz beállítása
- tartalomalapú elnevezési séma (TBD) használata
- Lehetővé teszi a név szabályozását valamilyen szintaxis használatával
Az új általános kiterjesztésű cast metódus továbbra sem működik a LINQ-ban
Probléma
A szerepkörök/bővítmények korábbi terveiben csak a metódus típusargumentumait lehetett explicit módon megadni.
Most azonban, hogy a klasszikus bővítménymetelyekről való látszólagos átmenetre összpontosítunk, az összes típusargumentumot explicit módon kell megadni.
Ez nem oldja meg a LINQ-ban a bővítmény cast metódusának használatával kapcsolatos problémát.
Módosítsuk a bővítmények funkcióját, hogy alkalmazkodjunk ehhez a forgatókönyvhöz? (válasz: nem, emiatt nem kell újra átgondolnunk a bővítmény kiterjesztés tervezését, LDM 2025-05-05)
A bővítményparaméter korlátozása egy bővítménytagon
Engedélyeznünk kell a következőket? (válasz: nem, ezt később lehet hozzáadni)
static class E
{
extension<T>(T t)
{
public void M<U>(U u) where T : C<U> { } // error: 'E.extension<T>(T).M<U>(U)' does not define type parameter 'T'
}
}
public class C<T> { }
Null lehetőség
-
Erősítse meg a jelenlegi kialakítást, azaz a maximális hordozhatóságot/kompatibilitást(válasz: igen, LDM 2025-04-17)
extension([System.Diagnostics.CodeAnalysis.DoesNotReturnIf(false)] bool b)
{
public void AssertTrue() => throw null!;
}
extension([System.Diagnostics.CodeAnalysis.NotNullIfNotNull("o")] ref int? i)
{
public void M(object? o) => throw null!;
}
Metadaták
A csontváz metódusoknak(válasz: igen, LDM 2025-04-17)NotSupportedExceptionvagy inkább valamilyen más szokásos kivételt kéne dobniuk (jelenleg ezt tesszükthrow null;)?Több paramétert is el kell fogadnunk a jelölőmetódusban a metaadatokban (abban az esetben, ha az új verziók több információt adnak hozzá)?(válasz: továbbra is szigorúak maradhatunk, LDM 2025-04-17)A bővítményjelölőt vagy a beszédes megvalósítási metódusokat speciális névvel kell megjelölni?(válasz: a marker metódust speciális névvel kell megjelölni, és ellenőrizni kell, de implementálási módszereket nem, LDM 2025-04-17)Akkor is hozzá kell adni(válasz: igen, LDM 2025-03-10)[Extension]attribútumot a statikus osztályhoz, ha nincs példánykiterjesztési módszer?Győződjön meg arról, hogy attribútumot kell hozzáadni(válasz: nem, LDM 2025-03-10)[Extension]a megvalósítási lekérésekhez és a setterekhez is.-
Győződjön meg arról, hogy a bővítménytípusokat speciális névvel kell megjelölni, és a fordítónak szüksége lesz erre a jelölőre a metaadatokban (ez az előzetes verziótól való kompatibilitástörő változás)(válasz: jóváhagyva, LDM 2025-06-23)
statikus gyár forgatókönyve
Mik a statikus metódusok ütközési szabályai?(válasz: meglévő C#-szabályok használata az elzárt statikus típushoz, relaxáció nélkül, LDM 2025-03-17)
Keresés
Hogyan oldhatóak meg a példánymetódus-meghívások most, hogy kimondható implementációnevek állnak rendelkezésünkre?Előnyben részesítjük a csontvázmetódust a megfelelő megvalósítási módszerrel.Statikus bővítménymetódusok feloldása(válasz: csakúgy, mint a példánykiterjesztési módszerek, LDM 2025-03-03)Hogyan oldható fel a tulajdonságok?(széles vonásokkal megválaszolva LDM 2025-03-03, de a jobbítás érdekében nyomonkövetést igényel)-
Hatókörkezelési és árnyékolási szabályok bővítményparaméterekhez és típusparaméterekhez(válasz: a bővítményblokk hatókörében, az árnyékolás nem engedélyezett, LDM 2025-03-10) Hogyan alkalmazható az ORPA az új bővítménymetelyekre?(válasz: a bővítményblokkokat transzparensként kezeli, az ORPA "tartalmazó típusa" az LDM 2025-04-17 nevű statikus osztályt foglalja magában)
public static class Extensions
{
extension(Type1)
{
[OverloadResolutionPriority(1)]
public void Overload(...)
}
extension(Type2)
{
public void Overload(...)
}
}
Az ORPA alkalmazható az új bővítménytulajdonságokra?(válasz: igen, és az ORPA-t másolni kell a megvalósítási módszereknél, LDM 2025-04-23)
public static class Extensions
{
extension(int[] i)
{
public P { get => }
}
extension(ReadOnlySpan<int> r)
{
[OverloadResolutionPriority(1)]
public P { get => }
}
}
- Hogyan lehet újból létrehozni a klasszikus bővítményfeloldási szabályokat? Megteszünk?
- frissítse a klasszikus bővítménymetelyek szabványát, és használja ezt az új bővítménymetelyek leírására is,
- A klasszikus bővítménymódszerek meglévő nyelvének megtartása, amely az új bővítménymódszerek leírására is használható, de mindkettő esetében ismert specifikáció eltérés van.
- Tartsa meg a klasszikus kiterjesztési módszerek meglévő nyelvét, de az új kiterjesztési módszerekhez használjon más nyelvet, és csak a klasszikus kiterjesztési módszerek esetében legyen ismert eltérés a specifikációtól.
-
Győződjön meg arról, hogy nem szeretnénk explicit típusú argumentumokat letiltani egy tulajdonsághozzáférésen(válasz: nincs tulajdonsághozzáférés explicit típusargumentumokkal, a WG-ben tárgyalt)
string s = "ran";
_ = s.P<object>; // error
static class E
{
extension<T>(T t)
{
public int P => 0;
}
}
-
Győződjön meg arról, hogy jobbsági szabályokat szeretnénk alkalmazni akkor is, ha a fogadó típus(válasz: a statikus bővítménytagok feloldásakor figyelembe kell venni egy csak típusra vonatkozó bővítményparamétert, LDM 2025-06-23)
int.M();
static class E1
{
extension(int)
{
public static void M() { }
}
}
static class E2
{
extension(in int i)
{
public static void M() => throw null;
}
}
-
Ellenőrizze, hogy elfogadjuk-e a kétértelműséget, amikor mindkét módszer és tulajdonság alkalmazható(válasz: egy javaslatot kell kidolgoznunk, amely meghaladja a jelenlegi helyzetet, .NET 10-re halasztva, LDM 2025-06-23) -
Győződjön meg arról, hogy nem törekszünk arra, hogy jobb megoldást találjunk az összes tag esetében, mielőtt meghatároznánk a nyertes tagtípust(válasz: punting out of .NET 10, WG 2025-07-02)
string s = null;
s.M(); // error
static class E
{
extension(string s)
{
public System.Action M => throw null;
}
extension(object o)
{
public string M() => throw null;
}
}
Rendelkezünk implicit fogadóval a bővítménydeklarációkban?(válasz: nem, korábban már szó volt az LDM-ben)
static class E
{
extension(object o)
{
public void M()
{
M2();
}
public void M2() { }
}
}
Engedélyeznünk kell a keresést a típusparaméteren?(vitafórum) (válasz: nem, várjuk a visszajelzést, LDM 2025-04-16)
Hozzáférhetőség
Mit jelent az akadálymentesség a bővítménydeklarációban?(válasz: a bővítménydeklarációk nem számítanak akadálymentességi hatókörnek, LDM 2025-03-17)Alkalmazzuk az "inkonzisztens hozzáférhetőség" ellenőrzést a vevőparaméterre, még akkor is, ha statikus tagokról van szó?(válasz: igen, LDM 2025-04-17)
public static class Extensions
{
extension(PrivateType p)
{
// We report inconsistent accessibility error,
// because we generate a `public static void M(PrivateType p)` implementation in enclosing type
public void M() { }
public static void M2() { } // should we also report here, even though not technically necessary?
}
private class PrivateType { }
}
Bővítménydeklaráció érvényesítése
Ellazítsuk a típusparaméter érvényesítését (következtetés: az összes típusparaméternek meg kell jelennie a bővítményparaméter típusában), ahol csak metódusok vannak?(válasz: igen, LDM 2025-04-06) Ezzel 100% klasszikus bővítési módszer portolását teszi lehetővé.
Ha vanTResult M<TResult, TSource>(this TSource source), portként használhatjaextension<TResult, TSource>(TSource source) { TResult M() ... }-t.Erősítse meg, hogy a bővítményekben engedélyezni kellene-e az init-only hozzáférőket(válasz: rendben van, egyelőre tiltsák le, LDM 2025-04-17)Engedélyezhető-e(válasz: nem, tartsa meg a specifikált szabályt, LDM 2025-03-24)extension(int receiver) { public void M2() {} }extension(ref int receiver) { public void M2() {} }az egyetlen különbség a fogadó ref-nessében?Panaszkodjunk egy ilyen(válasz: igen, tartsa meg a meghatározott szabályt, LDM 2025-03-24)extension(object receiver) { public int P1 => 1; }extension(object receiver) { public int P1 {set{}} }konfliktus miatt?Panaszkodjunk olyan csontváz metódusok közötti ütközésekről, amelyek nem jelentenek ütközést a megvalósítási módszerek között?(válasz: igen, tartsa meg a meghatározott szabályt, LDM 2025-03-24)
static class E
{
extension(object)
{
public void Method() { }
public static void Method() { }
}
}
Az aktuális ütközési szabályok a következők: 1. ellenőrizze, hogy nincs-e ütközés a hasonló bővítményeken belül osztály-/struktúraszabályok használatával, 2. ellenőrizze, hogy nincsenek-e ütközések a különböző bővítmény-deklarációk implementálási módszerei között.
Szükségünk van még a szabályok első részére?(válasz: igen, megtartjuk ezt a struktúrát, mivel segít az API-k fogyasztásában, LDM 2025-03-24)
XML-dokumentumok
Támogatott a(válasz: igen paramref bővítményparaméter engedélyezve van a bővítménytagokon, LDM 2025-05-05)paramrefvevő paraméter a bővítési tagoknál? Még statikusan is? Hogyan van kódolva a kimenetben? Valószínűleg a szabványos módszer<paramref name="..."/>egy ember számára is működik, de fennáll a veszélye annak, hogy egyes meglévő eszközök nem fogják boldogan megtalálni az API paraméterei között.Át kellene másolnunk a dokumentációs megjegyzéseket a megvalósítási eljárásokba, beszédes nevekkel?(válasz: nincs másolás, LDM 2025-05-05)A(válasz: nincs másolás, LDM 2025-05-05)<param>vevőparaméternek megfelelő elemet át kell másolni a bővítménytárolóból, ha példánymetódusokról van szó? Van még valami, amit át kell másolni a tárolóból a megvalósítási módszerekre (<typeparam>stb.) ?A(válasz: nem, egyelőre LDM 2025-05-05)<param>bővítményparamétert felülbírálásként kell engedélyezni a bővítménytagokon?- Megjelenne valahol a bővítményblokkok összegzése?
CREF
-
Szintaxis megerősítése(válasz: a javaslat jó, LDM 2025-06-09) Hivatkozhat-e bővítményblokkra ((válasz: nem, LDM 2025-06-09)E.extension(int))?Lehet-e olyan tagra hivatkozni, aki nem minősített szintaxist használ:(válasz: igen, LDM 2025-06-09)extension(int).Member?- Használjunk-e különböző karaktereket a kimondhatatlan névhez, hogy elkerüljük az XML-eket? (válasz: áthárítás a WG-re, LDM 2025-06-09)
Győződjön meg arról, hogy a vázra és a megvalósítási módszerekre való hivatkozás lehetséges:(válasz: igen, LDM 2025-06-09)E.Mvs.E.extension(int).M. Mindkettő szükségesnek tűnik (a kiterjesztési tulajdonságok és a klasszikus kiterjesztési metódusok hordozhatósága).Problémásak a bővítmény metaadatainak nevei a verziószámozási dokumentumok esetében?(válasz: igen, el fogunk mozdulni a sorszámoktól, és egy tartalomalapú stabil elnevezési sémát fogunk használni)
További tagtípusok támogatásának hozzáadása
Nem kell egyszerre implementálnunk ezt a kialakítást, hanem egy vagy néhány tagtípus szerinti megközelítést alkalmazhatunk. Az alapvető kódtárak ismert forgatókönyvei alapján a következő sorrendben kell dolgoznunk:
- Tulajdonságok és metódusok (példány és statikus)
- Működtetők
- Indexelők (példány és statikus, lehetőség szerint korábbi időpontban opportunista módon végrehajtva)
- Van még valami?
Mennyire szeretnénk előre átgondolni más típusú tagok számára a tervezést?
extension_member_declaration // add
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| finalizer_declaration
| static_constructor_declaration
| type_declaration
;
Beágyazott típusok
Ha úgy döntünk, hogy továbblépünk a bővítmények beágyazott típusaival, íme néhány megjegyzés a korábbi megbeszélésekből:
- Ütközés lenne, ha két bővítménydeklaráció egymásba ágyazott bővítménytípusokat deklarálna ugyanazokkal a névvel és aritással. Nincs megoldásunk a metaadatokban való ábrázoláshoz.
- Az általunk megvitatott durva megközelítés a metaadatokra vonatkozóan:
- egy csontvázba ágyazott típust bocsátanánk ki eredeti típusparaméterekkel és tagok nélkül
- egy beágyazott implementációtípust bocsátanánk ki a bővítménydeklarációban előre felírt típusparaméterekkel, valamint a forrásban megjelenő összes tag-implementációval (modulo a típusparaméterekre hivatkozik)
Konstruktorok
A konstruktorokat általában a C# példánytagjaként írják le, mivel a törzsük a kulcsszón keresztül fér hozzá az this újonnan létrehozott értékhez.
Ez azonban nem működik jól a példánybővítmény-tagok paraméteralapú megközelítéséhez, mivel paraméterként nincs előzményérték.
Ehelyett a bővítménykonstruktorok a statikus gyári módszerekhez hasonlóan működnek.
Statikus tagoknak minősülnek abban az értelemben, hogy nem függnek a fogadó paraméter nevétől.
A testüknek explicit módon létre kell hoznia és vissza kell adnia az építési eredményt.
Maga a tag továbbra is konstruktorszintaxissal van deklarálva, de nem rendelkezhet this vagy base inicializálókkal, és nem támaszkodik arra a fogadótípusra, amely akadálymentes konstruktorokkal rendelkezik.
Ez azt is jelenti, hogy a bővítménykonstruktorok olyan típusok esetében deklarálhatók, amelyek nem rendelkeznek saját konstruktorokkal, például interfészekkel és enumerálási típusokkal:
public static class Enumerable
{
extension(IEnumerable<int>)
{
public static IEnumerable(int start, int count) => Range(start, count);
}
public static IEnumerable<int> Range(int start, int count) { ... }
}
Lehetővé teszi:
var range = new IEnumerable<int>(1, 100);
Rövidebb űrlapok
A javasolt kialakítás elkerüli a fogadó specifikációinak tagonkénti ismétlődését, de végül a bővítménytagok két mélyen beágyazva lesznek egy statikus osztályba és egy bővítménydeklarációba. Valószínűleg gyakori, hogy a statikus osztályok csak egy kiterjesztési deklarációt tartalmaznak, vagy hogy a bővítménydeklarációk csak egy tagot tartalmaznak, és valószínűnek tűnik számunkra, hogy lehetővé tegyük ezeknek az eseteknek a szintaktikai rövidítését.
Statikus osztály- és bővítménydeklarációk egyesítése:
public static class EmptyExtensions : extension(IEnumerable source)
{
public bool IsEmpty => !source.GetEnumerator().MoveNext();
}
Ez végül jobban hasonlít arra, amit "típusalapú" megközelítésnek hívunk, ahol a bővítménytagok tárolója maga is el van nevezve.
Bővítmény deklarációjának és tagjának egyesítése:
public static class Bits
{
extension(ref ulong bits) public bool this[int index]
{
get => (bits & Mask(index)) != 0;
set => bits = value ? bits | Mask(index) : bits & ~Mask(index);
}
static ulong Mask(int index) => 1ul << index;
}
public static class Enumerable
{
extension<TSource>(IEnumerable<TSource> source) public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
}
Ez végül jobban hasonlít arra, amit "tagalapú" megközelítésnek hívunk, ahol minden bővítménytag saját fogadó specifikációt tartalmaz.
C# feature specifications