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.
A C# 9.0 mintaegyeztetésének néhány olyan fejlesztését fontolgatjuk, amelyek természetes szinergiával rendelkeznek, és jól működnek számos gyakori programozási probléma megoldásához:
- https://github.com/dotnet/csharplang/issues/2925 típusminták
- https://github.com/dotnet/csharplang/issues/1350 Zárójeles minták az új kombinátorok elsőbbsége érvényesítéséhez vagy kiemeléséhez
-
https://github.com/dotnet/csharplang/issues/1350 konjunktív
andminták, amelyekhez mindkét különböző minta egyezése szükséges; -
https://github.com/dotnet/csharplang/issues/1350 Diszjunktív
orminták, amelyek esetén a két különböző minta közül az egyiknek kell megfelelnie; -
https://github.com/dotnet/csharplang/issues/1350 tagadott
notminták, amelyek egy adott mintát igényelnek, hogy ne egyezzenek; és - https://github.com/dotnet/csharplang/issues/812 olyan relációs minták, amelyek megkövetelik, hogy a bemeneti érték kisebb legyen, kisebb vagy egyenlő, stb. egy adott konstansnál.
Zárójeles minták
A zárójeles minták lehetővé teszik, hogy a programozó zárójeleket helyezzen bármilyen minta köré. Ez nem annyira hasznos a C# 8.0 meglévő mintáinál, azonban az új minta-kombinátorok olyan elsőbbséget élveznek, amelyet a programozó felül szeretne bírálni.
primary_pattern
: parenthesized_pattern
| // all of the existing forms
;
parenthesized_pattern
: '(' pattern ')'
;
Típusminták
Mintaként engedélyezünk egy típust:
primary_pattern
: type-pattern
| // all of the existing forms
;
type_pattern
: type
;
Ez visszamenőleg módosítja a meglévő is-type-expression úgy, hogy az egy is-pattern-expression legyen, amelyben a minta egy típus-minta, annak ellenére, hogy a fordító által létrehozott szintaxisfát nem módosítanánk.
Az egyik apró megvalósítási probléma az, hogy ez a nyelvtan nem egyértelmű. Az olyan sztringek, mint a a.b, minősített névként (típuskörnyezetben) vagy pontozott kifejezésként (kifejezéskörnyezetben) elemezhetők. A fordító már képes a minősített nevet úgy kezelni, mint a pontozott kifejezést annak érdekében, hogy kezelni tudjon valamit, például e is Color.Red-hoz hasonlót. A fordító szemantikai elemzése tovább bővülne, hogy képes legyen egy (szintaktikai) állandó mintát (például pontozott kifejezést) típusként kötni, hogy a szerkezet támogatása érdekében azt kötött típusmintaként kezelje.
A módosítás után képes lesz írni
void M(object o1, object o2)
{
var t = (o1, o2);
if (t is (int, string)) {} // test if o1 is an int and o2 is a string
switch (o1) {
case int: break; // test if o1 is an int
case System.String: break; // test if o1 is a string
}
}
Relációs minták
A relációs minták lehetővé teszik a programozó számára, hogy kifejezze, hogy egy bemeneti értéknek meg kell felelnie egy relációs kényszernek egy állandó értékhez képest:
public static LifeStage LifeStageAtAge(int age) => age switch
{
< 0 => LifeStage.Prenatal,
< 2 => LifeStage.Infant,
< 4 => LifeStage.Toddler,
< 6 => LifeStage.EarlyChild,
< 12 => LifeStage.MiddleChild,
< 20 => LifeStage.Adolescent,
< 40 => LifeStage.EarlyAdult,
< 65 => LifeStage.MiddleAdult,
_ => LifeStage.LateAdult,
};
A relációs minták támogatják a relációs operátorokat, <, <=, >és >= minden olyan beépített típuson, amely támogatja az ilyen bináris relációs operátorokat két azonos típusú operandussal egy kifejezésben. Kifejezetten támogatjuk ezeket a relációs mintákat: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, nintés nuint.
primary_pattern
: relational_pattern
;
relational_pattern
: '<' relational_expression
| '<=' relational_expression
| '>' relational_expression
| '>=' relational_expression
;
A kifejezésnek állandó értéket kell eredményeznie. Hiba, ha az állandó érték double.NaN vagy float.NaN. Hiba, ha a kifejezés null állandó.
Ha a bemenet egy olyan típusú, amelyhez egy alkalmazható beépített bináris relációs operátor van meghatározva, ahol a bemenet a bal operandus és az adott állandó a jobb operandus, akkor az operátor kiértékelését a relációs minta jelentéseként kell értelmezni. Ellenkező esetben a bemenetet explicit null értékű vagy nemboxolásos átalakítással alakítjuk át a kifejezés típusára. Fordítási időpontban hiba lép fel, ha nincs ilyen átalakítás. A minta nem tekinthető egyezőnek, ha az átalakítás sikertelen. Ha az átalakítás sikeres, akkor a mintaegyeztetési művelet eredménye annak a kifejezésnek az kiértékelése, e OP v ahol e a konvertált bemenet, OP a relációs operátor, v pedig az állandó kifejezés.
Minta-kombinátorok
A mintakombinátorok lehetővé teszik két különböző mintának az egyidejű egyezését and használatával (ez a andismételt használatával bármilyen számú mintára kiterjeszthető), vagy az egyik a két különböző minta közül or használatával (u.a.), vagy egy minta not.
A kombinátor gyakori használata az idioma lesz
if (e is not null) ...
Olvashatóbb, mint a jelenlegi kifejezés e is object, ez a minta egyértelműen kifejezi, hogy nem nullérték ellenőrzéséről van szó.
A and és or kombinátorok hasznosak lesznek az értéktartományok teszteléséhez
bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
Ez a példa azt szemlélteti, hogy a and magasabb elemzési prioritással (azaz szorosabb kötéssel) rendelkezik, mint or. A programozó a zárójeles mintával az elsőbbséget explicitvá teheti:
bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
Mint minden minta, ezek a kombinátorok is használhatók minden olyan környezetben, amelyben minta várható, beleértve a beágyazott mintákat, a mintakifejezésként, a kapcsolókifejezésként, valamint a kapcsolóutasítás eset címkéjének mintáját.
pattern
: disjunctive_pattern
;
disjunctive_pattern
: disjunctive_pattern 'or' conjunctive_pattern
| conjunctive_pattern
;
conjunctive_pattern
: conjunctive_pattern 'and' negated_pattern
| negated_pattern
;
negated_pattern
: 'not' negated_pattern
| primary_pattern
;
primary_pattern
: // all of the patterns forms previously defined
;
Ugrás a 6.2.5-ös nyelvtani kétértelműségekre
A típusmintabevezetése miatt előfordulhat, hogy egy általános típus megjelenhet a jelölő =>előtt. Ezért a => a §6.2.5 Nyelvtani kétértelműségek listában felsorolt tokenek készletéhez kerül hozzáadásra, hogy lehetővé tegye a típusargumentumlistát kezdő < egyértelműsítését. Lásd még: https://github.com/dotnet/roslyn/issues/47614.
Javasolt módosításokkal kapcsolatos problémák megnyitása
Relációs operátorok szintaxisa
and, orés not valamilyen környezetfüggő kulcsszavak? Ha igen, akkor van-e kompatibilitástörő változás (például a deklarációs mintatervezőként való használatukhoz képest).
Szemantika (pl. típus) a relációs operátorok esetében
Várhatóan támogatni fogjuk az összes olyan primitív típust, amely relációs operátor használatával hasonlítható össze egy kifejezésben. A jelentés egyszerű esetekben egyértelmű
bool IsValidPercentage(int x) => x is >= 0 and <= 100;
De ha a bemenet nem ilyen primitív típus, milyen típusra próbáljuk átalakítani?
bool IsValidPercentage(object x) => x is >= 0 and <= 100;
Azt javasoltuk, hogy ha a bemeneti típus már összehasonlítható primitív, akkor ez az összehasonlítás típusa. Ha azonban a bemenet nem összehasonlítható primitív, akkor a relációt úgy kezeljük, mint ami implicit típustesztet tartalmaz a jobb oldalon lévő állandó típusára. Ha a programozó egynél több bemeneti típust kíván támogatni, ezt explicit módon kell elvégezni:
bool IsValidPercentage(object x) => x is
>= 0 and <= 100 or // integer tests
>= 0F and <= 100F or // float tests
>= 0D and <= 100D; // double tests
Eredmény: A relációs tartalmaz egy implicit típustesztet a relációs konstans típusára a relációs jobb oldalán.
A típusinformációk balról jobbra folynak a and-n
Azt javasolták, hogy amikor and kombinátort ír, a bal oldalon megszerzett felső szintű típussal kapcsolatos információk esetleg jobbra áramolhatnak. Például
bool isSmallByte(object o) => o is byte and < 100;
Itt a második mintára
- Ha
Ptípusminta, akkor a szűkített típus a típusminta típusának típusa. - Ha
Pdeklarációs minta, a szűkített típus a deklarációs minta típusának típusa. - Ha
Pegy rekurzív minta, amely explicit típust ad, a szűkített típus ez a típus. - Ha
a szabályaival egyezni, akkor a szűkített típus atípus. - Ha
Pegy olyan állandó minta, ahol az állandó nem null értékű, és ahol a kifejezés nem konvertálható állandó kifejezésként típusú bemenetre, akkor a szűkített típus az állandó típusa. - Ha
Polyan relációs minta, amelyben az állandó kifejezés nem konstanskifejezés-konverziósbemeneti típusra, akkor a szűkített típus az állandó típusa. - Ha
Porminta, akkor a szűkített típus az al-minták szűkített típusának közös típusa, ha létezik ilyen közös típus. Ebből a célból a közös típusú algoritmus csak az azonosság-, a boxing- és az implicit referencia konverziókat veszi figyelembe, és figyelembe veszi aorminták sorozatának minden részmintáit (figyelmen kívül hagyva a zárójeles mintákat). - Ha
Pegyandminta, akkor a szűkített típus a szűkített típus a megfelelő minta szerint. Ezenkívül a bal minta beszűkített típusa a jobb minta bemeneti típusa . - Ellenkező esetben a
PPbemeneti típusa.
Eredmény: A fenti szűkítő szemantikát implementáltuk.
Változódefiníciók és határozott hozzárendelés
A or és not minták hozzáadása érdekes új problémákat okoz a mintaváltozók és a határozott hozzárendelés körül. Mivel a változók általában legfeljebb egyszer deklarálhatók, úgy tűnik, hogy a or minta egyik oldalán deklarált mintaváltozók nem lesznek egyértelműen hozzárendelve, amikor a minta egyezik. Hasonlóképpen, egy not mintában deklarált változót nem kell feltétlenül hozzárendelni, amikor a minta egyezik. Ennek legegyszerűbb módja, ha megtiltja a mintaváltozók deklarálását ezekben a kontextusokban. Ez azonban túl korlátozó lehet. Más megközelítéseket is figyelembe kell venni.
Az egyik olyan forgatókönyv, amelyet érdemes megfontolni,
if (e is not int i) return;
M(i); // is i definitely assigned here?
Ez ma nem működik, mert egy is-pattern-expressionesetében a mintaváltozókat csak akkor tekintik egyértelműen hozzárendeltnek , ha az is-pattern-expression igaz ("határozottan hozzárendelve, ha igaz").
Ennek támogatása egyszerűbb lenne (a programozó szemszögéből), mint egy negated-condition if állítás támogatása. Még ha ilyen támogatást is adunk hozzá, a programozók csodálkoznának, hogy a fenti kódrészlet miért nem működik. Másrészt, ugyanez a forgatókönyv egy switch esetében kevésbé értelmes, mivel nincs megfelelő pont a programban, ahol a egyértelműen hozzá lett volna rendelve, amikor a hamis értelmes lett volna. Engedélyeznénk ezt egy is-pattern-expression-ben, de nem más olyan környezetekben, ahol a minták engedélyezettek? Ez szabálytalannak tűnik.
Ehhez kapcsolódik a határozott hozzárendelés problémája egy diszjunktív mintázatban.
if (e is 0 or int i)
{
M(i); // is i definitely assigned here?
}
Csak akkor várnánk, hogy a i biztosan hozzárendelésre kerüljön, ha a bemenet nem nulla. Mivel azonban nem tudjuk, hogy a bemenet nulla-e vagy sem a blokkon belül, i nincs egyértelműen hozzárendelve. Azonban, mi van akkor, ha lehetővé tesszük, hogy a i különböző, kölcsönösen kizáró mintákban legyen deklarálva?
if ((e1, e2) is (0, int i) or (int i, 0))
{
M(i);
}
Itt a i változó egyértelműen hozzá van rendelve a blokkon belül, és a nulla elem megtalálásakor a tuple másik elemétől veszi az értéket.
Azt is javasolták, hogy tegyék lehetővé a változók (többszöri) definiálását minden esetblokk esetében.
case (0, int x):
case (int x, 0):
Console.WriteLine(x);
A munka elvégzéséhez gondosan meg kell határoznunk, hogy hol engedélyezhetők ilyen több definíciók, és milyen feltételek mellett tekinthető egy ilyen változó egyértelműen hozzárendeltnek.
Ha úgy döntenénk, hogy későbbre halasztjuk az ilyen munkát (amit tanácsolok), akkor a C# 9-ben ezt mondhatnánk.
-
notvagyoralatt a mintaváltozók nem deklarálhatók.
Ezután lenne időnk olyan tapasztalatokat fejleszteni, amelyek betekintést nyújtanak a későbbi lazítás lehetséges értékébe.
Eredmény: A mintaváltozók nem deklarálhatók not vagy or minta alatt.
Diagnosztika, alárendelés és teljesség
Ezek az új mintaűrlapok számos új lehetőséget adnak a felismerhető programozói hibákra. El kell döntenünk, hogy milyen típusú hibákat diagnosztizálunk, és hogyan kell ezt megtenni. Íme néhány példa:
case >= 0 and <= 100D:
Ez az eset soha nem egyezhet meg (mivel a bemenet nem lehet int és doubleis). Már van egy hiba, amikor olyan esetet észlelünk, amely soha nem egyezik, de a megfogalmazása ("A kapcsoló esetet már egy korábbi eset kezelte" és "A mintát már kezelte a kapcsolókifejezés egy korábbi ága") új helyzetekben félrevezető lehet. Lehet, hogy módosítani kell a szövegezést, hogy csak azt mondjuk, hogy a minta soha nem egyezik a bemenettel.
case 1 and 2:
Hasonlóképpen, ez hiba lenne, mert egy érték nem lehet 1 és 2is.
case 1 or 2 or 3 or 1:
Ez az eset megfeleltethető, de a végén lévő or 1 nem ad értelmet a mintának. Azt javaslom, hogy törekedjünk arra, hogy hibát állítsunk elő, amikor egy összetett minta egy kötő- vagy disjunctja nem határoz meg egy mintaváltozót, vagy befolyásolja a megfeleltethető értékek készletét.
case < 2: break;
case 0 or 1 or 2 or 3 or 4 or 5: break;
Itt 0 or 1 or semmit sem ad hozzá a második esethez, mivel ezeket az értékeket az első eset kezelte volna. Ez is megérdemel egy hibát.
byte b = ...;
int x = b switch { <100 => 0, 100 => 1, 101 => 2, >101 => 3 };
Az ilyen kapcsolókifejezéseket teljes kell tekinteni (az összes lehetséges bemeneti értéket kezeli).
A C# 8.0-ban a byte típusú bemenettel rendelkező kapcsolókifejezések csak akkor tekinthetők kimerítőnek, ha olyan végleges kart tartalmaz, amelynek mintája mindennek megfelel (eldobott minta vagy var-minta). A C# 8-ban még az olyan kapcsolókifejezések sem tekinthetők teljesnek, amelyek minden különböző byte értékhez rendelkezik karnal. A relációs minták teljességének megfelelő kezeléséhez ezt az esetet is kezelni kell. Ez gyakorlatilag egy kompatibilitástörő változás lesz, de valószínűleg egy felhasználó sem fogja észrevenni.
C# feature specifications