Mintaegyeztetés áttekintése
A mintaegyeztetés egy olyan technika, amely során teszteli a kifejezéseket, hogy megállapítsa, rendelkezik-e bizonyos jellemzőkkel. A C#-mintaegyezés tömörebb szintaxist biztosít a kifejezések teszteléséhez és a kifejezések egyezése esetén végrehajtandó műveletekhez. A "is
kifejezés" támogatja a mintaegyezést egy kifejezés teszteléséhez, és feltételesen deklarál egy új változót a kifejezés eredményéhez. A "switch
kifejezés" lehetővé teszi a műveletek végrehajtását egy kifejezés első egyező mintája alapján. Ez a két kifejezés a minták gazdag szókészletét támogatja.
Ez a cikk áttekintést nyújt azokról a forgatókönyvekről, amelyekben mintaegyezést használhat. Ezek a technikák javíthatják a kód olvashatóságát és helyességét. Az összes alkalmazható minta teljes körű ismertetését a nyelvi referencia mintáiról szóló cikkben találja.
Null értékű ellenőrzések
A mintaegyeztetés egyik leggyakoribb forgatókönyve, hogy az értékek nem null
. A null értékű értéktípusokat tesztelheti és konvertálhatja annak alapjául szolgáló típussá null
, miközben az alábbi példát használja:
int? maybe = 12;
if (maybe is int number)
{
Console.WriteLine($"The nullable int 'maybe' has the value {number}");
}
else
{
Console.WriteLine("The nullable int 'maybe' doesn't hold a value");
}
Az előző kód egy deklarációs minta a változó típusának teszteléséhez és egy új változóhoz való hozzárendeléséhez. A nyelvi szabályok sokkal biztonságosabbá teszik ezt a technikát, mint sok más. A változó number
csak a záradék valódi részében érhető el és rendelhető hozzá if
. Ha máshol próbálja elérni, akár a else
záradékban, akár a if
blokk után, a fordító hibát ad ki. Másodszor, mivel nem az operátort használja, ez a ==
minta akkor működik, ha egy típus túlterheli az operátort ==
. Így ideális módszer a null referenciaértékek ellenőrzésére, a not
minta hozzáadásával:
string? message = ReadMessageOrDefault();
if (message is not null)
{
Console.WriteLine(message);
}
Az előző példa egy állandó mintát használt a változó null
összehasonlításához. Ez not
egy logikai minta , amely akkor egyezik, ha a negated minta nem egyezik.
Típustesztek
A mintaegyeztetés másik gyakori módja egy változó tesztelése, hogy kiderüljön, megfelel-e egy adott típusnak. Az alábbi kód például ellenőrzi, hogy egy változó nem null értékű-e, és implementálja-e az interfészt System.Collections.Generic.IList<T> . Ha igen, a ICollection<T>.Count lista tulajdonságát használja a középső index megkereséséhez. A deklarációs minta nem egyezik meg egy null
értékkel, függetlenül a változó fordítási idejének típusától. Az alábbi kód a null
nem implementálható IList
típusok elleni védelem mellett véd.
public static T MidPoint<T>(IEnumerable<T> sequence)
{
if (sequence is IList<T> list)
{
return list[list.Count / 2];
}
else if (sequence is null)
{
throw new ArgumentNullException(nameof(sequence), "Sequence can't be null.");
}
else
{
int halfLength = sequence.Count() / 2 - 1;
if (halfLength < 0) halfLength = 0;
return sequence.Skip(halfLength).First();
}
}
Ugyanezek a tesztek alkalmazhatók egy switch
kifejezésben egy változó több különböző típuson való teszteléséhez. Ezekkel az információkkal jobb algoritmusokat hozhat létre az adott futásidejű típus alapján.
Különálló értékek összehasonlítása
Tesztelhet egy változót is, hogy megkeressen egyezést adott értékeken. Az alábbi kód egy példát mutat be, amelyben egy enumerálásban deklarált összes lehetséges értéken tesztel egy értéket:
public State PerformOperation(Operation command) =>
command switch
{
Operation.SystemTest => RunDiagnostics(),
Operation.Start => StartSystem(),
Operation.Stop => StopSystem(),
Operation.Reset => ResetToReady(),
_ => throw new ArgumentException("Invalid enum value for command", nameof(command)),
};
Az előző példa egy enumerálás értékén alapuló metódusküldést mutat be. Az utolsó _
eset egy elvetési minta , amely megfelel az összes értéknek. Kezeli azokat a hibafeltételeket, amelyekben az érték nem egyezik meg a megadott enum
értékek egyikével. Ha kihagyja a kapcsolókart, a fordító figyelmezteti, hogy a mintakifejezés nem kezeli az összes lehetséges bemeneti értéket. Futásidőben a switch
kifejezés kivételt okoz, ha a vizsgált objektum nem egyezik a kapcsolókarok egyikével sem. Számállandókat használhat számértékek helyett. Ezt a hasonló technikát a parancsokat képviselő állandó sztringértékekhez is használhatja:
public State PerformOperation(string command) =>
command switch
{
"SystemTest" => RunDiagnostics(),
"Start" => StartSystem(),
"Stop" => StopSystem(),
"Reset" => ResetToReady(),
_ => throw new ArgumentException("Invalid string value for command", nameof(command)),
};
Az előző példa ugyanazt az algoritmust mutatja be, de sztringértékeket használ szám helyett. Ezt a forgatókönyvet akkor használná, ha az alkalmazás normál adatformátum helyett szöveges parancsokra válaszol. A C# 11-től kezdve állandó sztringértékek tesztelésére is használhat egy Span<char>
vagy egy függvényt ReadOnlySpan<char>
, ahogyan az alábbi példában látható:
public State PerformOperation(ReadOnlySpan<char> command) =>
command switch
{
"SystemTest" => RunDiagnostics(),
"Start" => StartSystem(),
"Stop" => StopSystem(),
"Reset" => ResetToReady(),
_ => throw new ArgumentException("Invalid string value for command", nameof(command)),
};
Ezekben a példákban az elvetési minta biztosítja, hogy minden bemenetet kezeljen. A fordító segít abban, hogy minden lehetséges bemeneti értéket kezeljen.
Relációs minták
Relációs minták használatával tesztelheti, hogyan hasonlít össze egy érték az állandókkal. A következő kód például a víz állapotát adja vissza a Fahrenheit hőmérséklete alapján:
string WaterState(int tempInFahrenheit) =>
tempInFahrenheit switch
{
(> 32) and (< 212) => "liquid",
< 32 => "solid",
> 212 => "gas",
32 => "solid/liquid transition",
212 => "liquid / gas transition",
};
Az előző kód a kötőjeles and
logikai mintát is bemutatja annak ellenőrzéséhez, hogy mindkét relációs minta egyezik-e. A disjunctive or
mintát is használhatja annak ellenőrzésére, hogy mindkét minta egyezik-e. A két relációs mintát zárójelek veszik körül, amelyeket bármilyen minta körül használhat az egyértelműség érdekében. Az utolsó két kapcsolókar kezeli az olvadási pont és a forráspont eseteit. E két kar nélkül a fordító figyelmezteti, hogy a logika nem fedi le az összes lehetséges bemenetet.
Az előző kód egy másik fontos funkciót is bemutat, amely a fordító mintául szolgáló kifejezéseket biztosít: A fordító figyelmezteti Önt, ha nem kezeli az összes bemeneti értéket. A fordító figyelmeztetést is ad, ha a kapcsolókar mintáját egy korábbi minta fedi le. Ez szabadságot biztosít a kifejezések újrabontására és átrendezésére. Ugyanez a kifejezés írásának másik módja a következő lehet:
string WaterState2(int tempInFahrenheit) =>
tempInFahrenheit switch
{
< 32 => "solid",
32 => "solid/liquid transition",
< 212 => "liquid",
212 => "liquid / gas transition",
_ => "gas",
};
Az előző példában, valamint az újrabontás vagy átrendezés legfontosabb tanulsága, hogy a fordító ellenőrzi, hogy a kód kezeli-e az összes lehetséges bemenetet.
Több bemenet
Az eddig tárgyalt minták mindegyike egy bemenetet ellenőrzött. Olyan mintákat írhat, amelyek egy objektum több tulajdonságát vizsgálják. Vegye figyelembe a következő Order
rekordot:
public record Order(int Items, decimal Cost);
Az előző pozíciórekordtípus két tagot deklarál explicit pozícióban. Először a Items
, majd a sorrend jelenik meg Cost
. További információ: Rekordok.
Az alábbi kód a tételek számát és egy kedvezményes ár kiszámítására vonatkozó rendelés értékét vizsgálja:
public decimal CalculateDiscount(Order order) =>
order switch
{
{ Items: > 10, Cost: > 1000.00m } => 0.10m,
{ Items: > 5, Cost: > 500.00m } => 0.05m,
{ Cost: > 250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
var someObject => 0m,
};
Az első két kar a Order
. A harmadik csak a költségeket vizsgálja. A következő ellenőrzés az , null
és a végleges megegyezik bármely más érték. Ha a Order
típus megfelelő Deconstruct
metódust határoz meg, kihagyhatja a tulajdonságneveket a mintából, és dekonstruálással megvizsgálhatja a tulajdonságokat:
public decimal CalculateDiscount(Order order) =>
order switch
{
( > 10, > 1000.00m) => 0.10m,
( > 5, > 50.00m) => 0.05m,
{ Cost: > 250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
var someObject => 0m,
};
Az előző kód azt a pozíciómintát mutatja be, ahol a tulajdonságok a kifejezéshez vannak lebontva.
Listaminták
Lista vagy tömb elemeit listaminta használatával ellenőrizheti. A listaminta azt jelenti, hogy egy mintát a sorozat bármely elemére alkalmazhat. Emellett alkalmazhatja az elvetési mintát (_
) bármely elemhez, vagy alkalmazhat egy szeletmintát nulla vagy több elemhez.
A listaminták értékes eszköznek számítanak, ha az adatok nem követik a szokásos struktúrát. Mintamegfeleltetéssel tesztelheti az adatok alakját és értékeit ahelyett, hogy objektumkészletté alakítná őket.
Vegye figyelembe a banki tranzakciókat tartalmazó szövegfájl alábbi részletét:
04-01-2020, DEPOSIT, Initial deposit, 2250.00
04-15-2020, DEPOSIT, Refund, 125.65
04-18-2020, DEPOSIT, Paycheck, 825.65
04-22-2020, WITHDRAWAL, Debit, Groceries, 255.73
05-01-2020, WITHDRAWAL, #1102, Rent, apt, 2100.00
05-02-2020, INTEREST, 0.65
05-07-2020, WITHDRAWAL, Debit, Movies, 12.57
04-15-2020, FEE, 5.55
Ez egy CSV formátum, de egyes sorok több oszlopot tartalmaznak, mint mások. A feldolgozás szempontjából még rosszabb, hogy a típus egyik oszlopa WITHDRAWAL
felhasználó által létrehozott szöveget tartalmaz, és vesszőt tartalmazhat a szövegben. Listaminta, amely tartalmazza az elvetési mintát, az állandó mintát és a varmintát az érték ilyen formátumban történő rögzítéséhez:
decimal balance = 0m;
foreach (string[] transaction in ReadRecords())
{
balance += transaction switch
{
[_, "DEPOSIT", _, var amount] => decimal.Parse(amount),
[_, "WITHDRAWAL", .., var amount] => -decimal.Parse(amount),
[_, "INTEREST", var amount] => decimal.Parse(amount),
[_, "FEE", var fee] => -decimal.Parse(fee),
_ => throw new InvalidOperationException($"Record {string.Join(", ", transaction)} is not in the expected format!"),
};
Console.WriteLine($"Record: {string.Join(", ", transaction)}, New balance: {balance:C}");
}
Az előző példa egy sztringtömböt vesz fel, amelyben minden elem egy mező a sorban. A switch
második mező kifejezéskulcsai, amelyek meghatározzák a tranzakció típusát és a fennmaradó oszlopok számát. Minden sor biztosítja, hogy az adatok a megfelelő formátumban legyen. Az elvetési minta (_
) kihagyja az első mezőt a tranzakció dátumával. A második mező megegyezik a tranzakció típusával. A fennmaradó elem egyezések az összeggel együtt ugorjanak a mezőre. Az utolsó egyezés a var mintával rögzíti az összeg sztringképét. A kifejezés kiszámítja a hozzáadni vagy kivonni kívánt összeget az egyenlegből.
A listaminták lehetővé teszik az adatelemek sorozatának egyezését. Az elvetési és szeletelési mintákat az elemek helyének megfelelően használhatja. Más mintákat használ az egyes elemek jellemzőinek egyeztetéséhez.
Ez a cikk bemutatja, hogy milyen típusú kódokat írhat a C#-beli mintaegyezéssel. Az alábbi cikkek további példákat mutatnak be a minták forgatókönyvekben való használatára, valamint a használható minták teljes szókészletét.
Lásd még
- Mintaegyezés használata az "is" ellenőrzés és a leadás (stílusszabályok IDE0020 és IDE0038) elkerüléséhez
- Feltárás: Mintamegfeleltetés használata az osztály viselkedésének kialakításához a jobb kód érdekében
- Oktatóanyag: Mintamegfeleltetés használata típusalapú és adatvezérelt algoritmusok létrehozásához
- Referencia: Mintaegyezés
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: