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.
Jegyzet
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.
Bajnoki probléma: https://github.com/dotnet/csharplang/issues/4934
Összefoglalás
Javasolt módosítások:
- Attribútumokkal rendelkező lambdák engedélyezése
- Explicit visszatérési típussal rendelkező lambdák használatának engedélyezése
- A lambdas és a metóduscsoportok természetes delegálási típusának következtetése
Motiváció
A lambdas attribútumainak támogatása paritásos metódusokat és helyi függvényeket biztosítana.
Az explicit visszatérési típusok támogatása szimmetriát biztosít a lambda paraméterekkel, ahol explicit típusok határozhatók meg. Az explicit visszatérési típusok engedélyezésével szabályozható a fordító teljesítménye a beágyazott lambdákban is, ahol a túlterhelés feloldásának meg kell kötnie a lambda törzsét az aláírás meghatározásához.
A lambdakifejezések és metóduscsoportok természetes típusa több olyan forgatókönyvet tesz lehetővé, ahol a lambdák és metóduscsoportok explicit delegálási típus nélkül is használhatók, beleértve inicializálóként var deklarációkban.
A lambdák és metóduscsoportok explicit delegálási típusainak megkövetelése nehézséget okoz az ügyfelek számára, és a közelmúltban akadályt jelentett az ASP.NET MapActionfejlesztésében.
ASP.NET MapAction javasolt módosítások nélkül (MapAction()System.Delegate argumentumot használ):
[HttpGet("/")] Todo GetTodo() => new(Id: 0, Name: "Name");
app.MapAction((Func<Todo>)GetTodo);
[HttpPost("/")] Todo PostTodo([FromBody] Todo todo) => todo;
app.MapAction((Func<Todo, Todo>)PostTodo);
ASP.NET MapAction a metóduscsoportok természetes típusaival:
[HttpGet("/")] Todo GetTodo() => new(Id: 0, Name: "Name");
app.MapAction(GetTodo);
[HttpPost("/")] Todo PostTodo([FromBody] Todo todo) => todo;
app.MapAction(PostTodo);
ASP.NET MapAction a lambda kifejezések attribútumaival és természetes típusaival:
app.MapAction([HttpGet("/")] () => new Todo(Id: 0, Name: "Name"));
app.MapAction([HttpPost("/")] ([FromBody] Todo todo) => todo);
Attribútumok
Attribútumok hozzáadhatók a lambda kifejezésekhez és a lambda paraméterekhez. A metódusattribútumok és a paraméterattribútumok közötti kétértelműség elkerülése érdekében az attribútumokat tartalmazó lambda kifejezésnek zárójeles paraméterlistát kell használnia. Nincs szükség paramétertípusokra.
f = [A] () => { }; // [A] lambda
f = [return:A] x => x; // syntax error at '=>'
f = [return:A] (x) => x; // [A] lambda
f = [A] static x => x; // syntax error at '=>'
f = ([A] x) => x; // [A] x
f = ([A] ref int x) => x; // [A] x
Több attribútum is megadható, vesszővel elválasztva ugyanabban az attribútumlistában, vagy különálló attribútumlistákként.
var f = [A1, A2][A3] () => { }; // ok
var g = ([A1][A2, A3] int x) => x; // ok
A szintaxissal deklarált névtelen metódusokhoz delegate { }.
f = [A] delegate { return 1; }; // syntax error at 'delegate'
f = delegate ([A] int x) { return x; }; // syntax error at '['
Az elemző előre meg fogja különböztetni a lambda kifejezést tartalmazó, illetve az elem-hozzárendeléssel rendelkező gyűjteményinicializálót.
var y = new C { [A] = x }; // ok: y[A] = x
var z = new C { [A] x => x }; // ok: z[0] = [A] x => x
Az elemző a ?[ egy feltételes elem hozzáférésének kezdeteként fogja kezelni.
x = b ? [A]; // ok
y = b ? [A] () => { } : z; // syntax error at '('
A lambda kifejezés vagy a lambda paraméterek attribútumai a metódus metaadataiba kerülnek, amely a lambdára van leképezve.
Az ügyfelek általában ne támaszkodjanak arra, hogy a lambda-kifejezések és a helyi függvények hogyan képeznek le forrásról metaadatra. A lambdas és a helyi függvények kibocsátása változhat a fordítóverziók között.
Az itt javasolt módosítások a Delegate vezérelt forgatókönyvre irányulnak.
Lehetőségnek kell lennie a MethodInfo példányhoz társított Delegate vizsgálatára a lambda kifejezés vagy helyi függvény aláírásának meghatározására, beleértve a fordító által kibocsátott explicit attribútumokat és további metaadatokat, mint például az alapértelmezett paramétereket.
Ez lehetővé teszi az olyan csapatok számára, mint a ASP.NET, hogy ugyanazokat a viselkedéseket tegyék elérhetővé a lambdákhoz és a helyi funkciókhoz, mint a szokásos módszerek.
Explicit visszatérési típus
A zárójeles paraméterlista előtt explicit visszatérési típus adható meg.
f = T () => default; // ok
f = short x => 1; // syntax error at '=>'
f = ref int (ref int x) => ref x; // ok
f = static void (_) => { }; // ok
f = async async (async async) => async; // ok?
Az elemző előre meg fogja különböztetni a metódushívást T() a lambda kifejezéstől T () => e.
Az explicit visszatérési típusok nem támogatottak a delegate { } szintaxissal deklarált névtelen metódusok esetében.
f = delegate int { return 1; }; // syntax error
f = delegate int (int x) { return x; }; // syntax error
A metódustípus-következtetésnek pontos következtetést kell végeznie egy explicit lambda visszatérési típusból.
static void F<T>(Func<T, T> f) { ... }
F(int (i) => i); // Func<int, int>
A lambda visszatérési típustól a delegált visszatérési típusig (a paramétertípusokhoz hasonló viselkedésnek megfelelő) varianciakonverziók nem engedélyezettek.
Func<object> f1 = string () => null; // error
Func<object?> f2 = object () => x; // warning
Az elemző lehetővé teszi a lambda kifejezéseket, amelyek ref típusú eredménnyel bírnak, további zárójelek nélkül a kifejezéseken belül.
d = ref int () => x; // d = (ref int () => x)
F(ref int () => x); // F((ref int () => x))
var nem használható explicit visszatérési típusként a lambda kifejezésekhez.
class var { }
d = var (var v) => v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = @var (var v) => v; // ok
d = ref var (ref var v) => ref v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = ref @var (ref var v) => ref v; // ok
Természetes (függvény) típus
Egy névtelen függvény kifejezés (§12.19) (lambda kifejezés vagy névtelen metódus) természetes típussal rendelkezik, ha a paraméterek típusa explicit, és a visszatérési típus explicit vagy következtethető (lásd §12.6.3.13).
A metóduscsoportok természetes típusúak, ha a metóduscsoport összes jelölt metódusa közös aláírással rendelkezik. (Ha a metóduscsoport bővítménymetódusokat is tartalmazhat, a jelöltek közé tartozik az azt tartalmazó típus és az összes bővítménymetódus-hatókör.)
A névtelen függvénykifejezések vagy metóduscsoportok természetes típusa egy function_type. A function_type egy metódus-aláírást jelöl: a paramétertípusokat és a ref típusokat, valamint a visszatérési típust és a ref típust. Az azonos aláírású névtelen függvénykifejezések vagy metóduscsoportok ugyanazokkal a function_typerendelkeznek.
Function_types csak néhány konkrét környezetben használhatók:
- implicit és explicit konverziók
- metódustípus-következtetés (§12.6.3) és a leggyakoribb típus (§12.6.3.15)
-
varinicializálók
A function_type csak fordítási időpontban létezik: function_types nem jelennek meg a forrásban vagy a metaadatokban.
Konverziók
Egy function_typeF esetén implicit function_type konverziók léteznek.
- Egy function_type
G, ha aFparaméterei és visszatérési típusai variancia-átalakíthatóak aGparamétereire és visszatérési típusára. - A
System.MulticastDelegatevagy aSystem.MulticastDelegatealaposztályai vagy felületei -
System.Linq.Expressions.ExpressionvagySystem.Linq.Expressions.LambdaExpression
A névtelen függvénykifejezések és metóduscsoportok már rendelkeznek konverzióval a kifejezés delegált típusokra és kifejezésfa típusokra (lásd a névtelen függvénykonverziókat a 10.7. §- és a metóduscsoport konverziókat a 10.8). Ezek a konverziók elegendőek ahhoz, hogy erős típusú delegált típusokká és kifejezésfatípusokká alakíthatók. A fenti function_type átalakítások csak típusú átalakításokat adnak hozzá az alaptípusokhoz: System.MulticastDelegate, System.Linq.Expressions.Expressionstb.
A function_typetípustól eltérő típusból function_type konvertálása nem történt. A function_types nincsenek explicit konverziók, mivel function_types nem hivatkozhatók a forrásban.
A System.MulticastDelegate vagy alaptípusra vagy felületre való átalakítás a névtelen függvényt vagy metóduscsoportot egy megfelelő delegálttípus példányaként valósítja meg.
A System.Linq.Expressions.Expression<TDelegate> vagy alaptípusra való átalakítás a lambda kifejezést kifejezésfaként valósítja meg, megfelelő delegálási típussal.
Delegate d = delegate (object obj) { }; // Action<object>
Expression e = () => ""; // Expression<Func<string>>
object o = "".Clone; // Func<object>
Függvény_típus konverziók nem implicit vagy explicit standard konverziók §10.4, és nem veszik figyelembe annak meghatározásakor, hogy a felhasználó által definiált konverziós operátor alkalmazható-e egy névtelen függvényre vagy metóduscsoportra. A felhasználó által definiált konverziók kiértékelése 10.5.3:
Ahhoz, hogy egy konverziós operátor alkalmazható legyen, a forrástípustól az operátor operandus típusára szabványos átalakítást (§10.4) kell végrehajtani, és az operátor eredménytípusától a céltípusig szabványos átalakítást kell végrehajtani.
class C
{
public static implicit operator C(Delegate d) { ... }
}
C c;
c = () => 1; // error: cannot convert lambda expression to type 'C'
c = (C)(() => 2); // error: cannot convert lambda expression to type 'C'
Figyelmeztetést jelent egy metóduscsoport implicit átalakítása object, mivel az átalakítás érvényes, de talán nem szándékos.
Random r = new Random();
object obj;
obj = r.NextDouble; // warning: Converting method group to 'object'. Did you intend to invoke the method?
obj = (object)r.NextDouble; // ok
Típuskövetkeztetés
A típuskövetkeztetés meglévő szabályai többnyire változatlanok (lásd 12.6.3. §-t). Az alábbiakban azonban néhány változás bizonyos típus következtetési fázisokra vonatkozik.
Első fázis
Az első fázis (§12.6.3.2) lehetővé teszi egy névtelen függvény számára, hogy Ti-höz kötődjön akkor is, ha Ti nem delegált vagy kifejezésfa típusú (például System.Delegate-re korlátozott típusparaméter esetén).
Az egyes metódusargumentumok
Ei:
- Ha
Einévtelen függvény, ésTidelegált típusú vagy kifejezésfa típusú, explicit paramétertípus-következtetésiEiTi, és explicit visszatérési típusú következtetésiEiTi.- Ellenkező esetben, ha
EitípusúUésxiegy értékparaméter, akkorTi.- Ellenkező esetben, ha
EiUtípussal rendelkezik ésxiegyrefvagyoutparaméter, akkor egy pontos következtetési történikU-rőlTi-re.- Ellenkező esetben nincs következtetés az argumentumra vonatkozóan.
Explicit visszatérési típus következtetési
Egy explicit visszatérési típus következtetése kifejezésből
ETa következő módon:
- Ha a
Eegy névtelen függvény explicit visszatérési típussalUr, ésTdelegált típusú vagy kifejezésfa típusú,Vrvisszatérési típussal, akkor pontos következtetési (§12.6.3.9)UrVr.
Javítás
A javítás (§12.6.3.12) biztosítja, hogy az egyéb átalakítások előnyben részesüljenek a típusú_funkció átalakításokkal szemben. (A Lambda-kifejezések és a metóduscsoport-kifejezések csak az alacsonyabb határokhoz járulnak hozzá, ezért a function_types kezelése csak az alacsonyabb határokhoz szükséges.)
Az nem rögzített típusú változók
Xikötöttek, rögzített az alábbiak szerint:
- A jelölttípusok
Ujhalmaza aXikötött halmaz összes típusának halmazaként kezdődik, ahol a függvénytípusok figyelmen kívül lesznek hagyva az alsó határban, ha vannak olyan típusok, amelyek nem függvénytípusok.- Ezután a
Ximindegyik korlátját megvizsgáljuk: AUminden pontosXihatárát minden olyanUjjelölttípus el lesz távolítva, amely nem azonos aU-gyel. Minden egyes alsó határUesetén aXi-beli minden olyan típusUjeltávolításra kerül a jelöltkészletből, amelyhez nem vezet egy implicit konverzióU-ből. AUminden felső kötöttXiminden olyanUj, amelyből nem azUimplicit konvertálása törlődik a jelöltkészletből.- Ha a többi jelölttípus között
Ujvan egy egyediV, amelyből implicit átalakítás történik az összes többi jelölttípusra, akkorXiV.- Ellenkező esetben a típus kielemezés sikertelen.
Leggyakoribb típus
A legjobb közös típus (§12.6.3.15) típuskövetkezési szempontból van meghatározva, így a fenti típuskövetkezési változások a leggyakoribb típusra is érvényesek.
var fs = new[] { (string s) => s.Length, (string s) => int.Parse(s) }; // Func<string, int>[]
var
A függvénytípusokkal rendelkező névtelen függvények és metóduscsoportok inicializálóként használhatók var deklarációkban.
var f1 = () => default; // error: cannot infer type
var f2 = x => x; // error: cannot infer type
var f3 = () => 1; // System.Func<int>
var f4 = string () => null; // System.Func<string>
var f5 = delegate (object o) { }; // System.Action<object>
static void F1() { }
static void F1<T>(this T t) { }
static void F2(this string s) { }
var f6 = F1; // error: multiple methods
var f7 = "".F1; // error: the delegate type could not be inferred
var f8 = F2; // System.Action<string>
A függvénytípusok nem használhatók a hozzárendelésekben az elvetésekhez.
d = () => 0; // ok
_ = () => 1; // error
Delegálási típusok
A névtelen függvény vagy metóduscsoport delegált típusa paramétertípusokkal P1, ..., Pn és visszatérési típus R a következő:
- ha bármely paraméter vagy visszatérési érték nem érték szerint van átvéve, vagy több mint 16 paraméter van, vagy bármelyik paramétertípus vagy visszatérési érték nem érvényes típusargumentum (például
(int* p) => { }), akkor a delegált egy szintetizáltinternalnévtelen delegált típus lesz, amelynek aláírása megegyezik a névtelen függvénnyel vagy metóduscsoporttal, és a paraméternevekarg1, ..., argnvagyarg, ha csak egy paraméter van. - ha
Rvoid, akkor a meghatalmazott típusaSystem.Action<P1, ..., Pn>; - ellenkező esetben a delegálás típusa
System.Func<P1, ..., Pn, R>.
A fordító lehetővé teheti, hogy a jövőben több aláírás kötődjön System.Action<> és System.Func<> típusokhoz (ha például ref struct típusargumentumok engedélyezettek).
A metóduscsoport-aláírásban a modopt() vagy modreq() figyelmen kívül van hagyva a megfelelő delegált típusban.
Ha ugyanabban a fordításban két névtelen függvény vagy metóduscsoport azonos paramétertípusokkal és módosítókkal rendelkező szintetizált delegálttípusokat, valamint azonos visszatérési típust és módosítókat igényel, a fordító ugyanazt a szintetizált delegálttípust fogja használni.
Túlterhelés feloldás
A jobb függvénytag (§12.6.4.3) frissítése olyan tagokat részesít előnyben, amelyeknél sem az átalakítások, sem a lambda kifejezésekből vagy metóduscsoportokból származó típusargumentumok nem foglalnak magukban következtetett típusokat.
Jobb függvénytag
... Egy argumentumlistát
A, amely tartalmaz egy argumentumkifejezés készletet{E1, E2, ..., En}, és két alkalmazható függvénytagotMpésMqa{P1, P2, ..., Pn}és{Q1, Q2, ..., Qn}paramétertípusokkal,Mphatároz meg, mint jobb függvénytagot, mintMq
- az egyes argumentumok esetén a
Ex-rőlPx-re történő implicit átalakítás nem minősül típusú függvényátalakításnakés
Mpnem általános metódus, vagyMpegy általános metódus, amely{X1, X2, ..., Xp}típusparamétereket használ, és minden típusparaméterhezXia típusargumentum kifejezésből vagy egy function_typekívüli típusból származik, és- legalább egy argumentum esetében a
Ex-rőlQx-re történő implicit átalakítás egy function_type_conversion, vagy haMqegy generikus metódus, amely típusparamétereket tartalmaz{Y1, Y2, ..., Yq}, és legalább az egyik típusparaméter eseténYia típusargumentum egy function_typealapján van kikövetkeztetve, vagy- minden argumentum esetében az
ExésQxközötti implicit átalakítás nem jobb, mint aExésPxközötti implicit átalakítás, és legalább egy argumentum esetében aEx-rőlPx-ra való átalakítás jobb, mint aEx-rólQx- ra történő átalakítás.
A kifejezésből való jobb átalakítás (12.6.4.5) frissül, hogy az olyan konverziókat részesítse előnyben, amelyek nem tartalmaztak a lambda-kifejezésekből vagy metóduscsoportokból származó következtetési típusokat.
Jobb konverzió a kifejezésből
Adott egy implicit konverzió
C1, amely kifejezéstEaT1típusra konvertál, és egy másik implicit konverzióC2, amely kifejezéstEaT2típusra konvertál, ekkorC1egy jobb konverzió, mintC2, ha:
C1nem function_type_conversion, ésC2function_type_conversionvagyEnem állandó interpolated_string_expression,C1egy implicit_string_handler_conversion,T1egy applicable_interpolated_string_handler_type, ésC2nem egy implicit_string_handler_conversion, vagyEnem egyezik meg pontosanT2-gyel, és legalább az alábbiak egyike igaz:
Szintaxis
lambda_expression
: modifier* identifier '=>' (block | expression)
| attribute_list* modifier* type? lambda_parameters '=>' (block | expression)
;
lambda_parameters
: lambda_parameter
| '(' (lambda_parameter (',' lambda_parameter)*)? ')'
;
lambda_parameter
: identifier
| attribute_list* modifier* type? identifier equals_value_clause?
;
Nyitott problémák
Az alapértelmezett értékeket támogatni kell-e a lambda kifejezésparaméterek esetében a teljesség érdekében?
Legyen System.Diagnostics.ConditionalAttribute tiltva a lambdakifejezések esetében, hiszen kevés olyan forgatókönyv létezik, ahol egy lambdakifejezés feltételesen használható?
([Conditional("DEBUG")] static (x, y) => Assert(x == y))(a, b); // ok?
Elérhetővé kell-e tenni a function_type a fordító API-ból az eredményként kapott delegálási típuson kívül?
A kikövetkeztetett delegált típus jelenleg a(z) System.Action<> vagy System.Func<>-t használja, ha a paraméter- és visszatérési típusok érvényes típusargumentumok, a(z) és, legfeljebb 16 paraméter van, és ha a várt Action<> vagy Func<> típus hiányzik, hibaüzenet jelenik meg. Ehelyett a fordítónak System.Action<> vagy System.Func<> kell használnia a ritkaságtól függetlenül? És ha a várt típus hiányzik, szintetizáljon egy delegált típust egyébként?
C# feature specifications