Mintaegyezés – a is kifejezések és switch operátorok and, orvalamint not a minták

is A kifejezés, a kapcsolóutasítás és a kapcsolókifejezés használatával tetszőleges számú jellemzővel egyező beviteli kifejezést használhat. A C# több mintát is támogat, beleértve a deklarációt, a típust, az állandót, a relációs, a tulajdonságot, a listát, a var- és az elvetési mintát. A mintákat a logikai logikai kulcsszavak és a logikai kulcsszavak andornothasználatával kombinálhatja.

A C# nyelv referenciadokumentuma a C# nyelv legújabb kiadású verzióját ismerteti. Emellett a közelgő nyelvi kiadás nyilvános előzetes verziójú funkcióinak kezdeti dokumentációját is tartalmazza.

A dokumentáció azonosítja azokat a funkciókat, amelyeket először a nyelv utolsó három verziójában vagy az aktuális nyilvános előzetes verziókban vezetnek be.

Tipp.

Ha meg szeretné tudni, hogy mikor jelent meg először egy funkció a C#-ban, tekintse meg a C# nyelvi verzióelőzményeiről szóló cikket.

A következő C#-kifejezések és -utasítások támogatják a mintaegyezést:

Ezekben a szerkezetekben egy bemeneti kifejezést az alábbi minták bármelyikével egyezhet meg:

  • Deklarációs minta: ellenőrizze egy kifejezés futásidejű típusát, és ha egy egyezés sikeres, rendeljen hozzá egy kifejezés eredményét egy deklarált változóhoz.
  • Típusminta: egy kifejezés futásidejű típusának ellenőrzése.
  • Állandó minta: annak tesztelése, hogy egy kifejezés eredménye egy megadott állandóval egyenlő-e.
  • Relációs minták: egy kifejezés eredményének összehasonlítása egy megadott állandóval.
  • Logikai minták: annak tesztelése, hogy egy kifejezés megfelel-e a minták logikai kombinációjának.
  • Tulajdonságminta: annak ellenőrzése, hogy egy kifejezés tulajdonságai vagy mezői megegyeznek-e a beágyazott mintákkal.
  • Pozícióminta: bontsa ki a kifejezés eredményét, és ellenőrizze, hogy az eredményként kapott értékek megfelelnek-e a beágyazott mintáknak.
  • var minta: egyezzen egy kifejezéssel, és rendelje hozzá az eredményét egy deklarált változóhoz.
  • Elvetési minta: egyezik bármely kifejezéssel.
  • Listaminták: annak tesztelése, hogy az elemek sorozata megfelel-e a megfelelő beágyazott mintáknak.

A logikai, tulajdonság-, pozíció- és listaminták rekurzív minták. Vagyis beágyazott mintákat tartalmazhatnak.

Példa arra, hogyan használhatók ezek a minták adatvezérelt algoritmusok létrehozásához : Oktatóanyag: Típusalapú és adatvezérelt algoritmusok létrehozása mintaegyeztetés használatával.

Deklarálási és típusminták

Deklarációs és típusminták használatával ellenőrizze, hogy egy kifejezés futásidejű típusa kompatibilis-e egy adott típussal. Deklarációs minta használatával új helyi változót is deklarálhat. Ha egy deklarációs minta megfelel egy kifejezésnek, hozzárendeli a változót a konvertált kifejezés eredményéhez, ahogy az alábbi példa is mutatja:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

A akkor egyezik meg egy kifejezéssel, ha egy kifejezés eredménye nem null, és az alábbi feltételek bármelyike igaz:

  • A kifejezés eredményének futásidejű típusa identitásátalakítással T rendelkezik.
  • A típus T egy ref struct típus, és a kifejezésből Tidentitásátalakítás történik.
  • A kifejezés eredményének futásidejű típusa típusból T, implementálási felületből Tszármazik, vagy egy másik implicit referenciakonvertálás létezik belőle T. Ez a feltétel az öröklési kapcsolatokat és a felület implementációit fedi le. Az alábbi példa két esetet mutat be, amikor ez a feltétel igaz:
    var numbers = new int[] { 10, 20, 30 };
    Console.WriteLine(GetSourceLabel(numbers));  // output: 1
    
    var letters = new List<char> { 'a', 'b', 'c', 'd' };
    Console.WriteLine(GetSourceLabel(letters));  // output: 2
    
    static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
    {
        Array array => 1,
        ICollection<T> collection => 2,
        _ => 3,
    };
    
    Az előző példában a metódus első hívásánál GetSourceLabel az első minta egy argumentumértéknek felel meg, mert az argumentum futásidejének típusa int[] a Array típusból származik. A metódus második hívásánál GetSourceLabel az argumentum futásidejének típusa List<T> nem a Array típusból származik, hanem implementálja az interfészt ICollection<T> .
  • A kifejezés eredményének futásidejű típusa egy nullázható értéktípus az alapul szolgáló típussal T, és a Nullable<T>.HasValue jellemzője a true.
  • A kifejezés eredményének futásidejű típusából boxing vagy unboxing konverzió létezhet T típusba, ha a kifejezés nem egy ref struct példány.

A deklarációs minták nem veszik figyelembe a felhasználó által definiált konverziókat vagy implicit span konverziókat.

Az alábbi példa az utolsó két feltételt mutatja be:

int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
    Console.WriteLine(a + b);  // output: 30
}

Ha csak a kifejezés típusát szeretné ellenőrizni, használja a változó nevének elvetésével _ , ahogy az alábbi példa is mutatja:

public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public static class TollCalculator
{
    public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
    {
        Car _ => 2.00m,
        Truck _ => 7.50m,
        null => throw new ArgumentNullException(nameof(vehicle)),
        _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
    };
}

Ehhez használjon típusmintát, ahogy az alábbi példa is mutatja:

public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
    Car => 2.00m,
    Truck => 7.50m,
    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};

A deklarációs mintához hasonlóan a típusminta akkor felel meg egy kifejezésnek, ha egy kifejezés eredménye nem null, és futásideje megfelel az előző feltételek bármelyikének.

A nem null érték ellenőrzéséhez használjon negatednullkonstansmintát, ahogy az alábbi példa is mutatja:

if (input is not null)
{
    // ...
}

További információkért tekintse meg a C#-nyelv specifikációjának Deklarációs mintája és Típusminta szakaszát.

Állandó minta

Az állandó minta egy alternatív szintaxis == , ha a megfelelő operandus állandó. Állandó mintával tesztelheti, hogy egy kifejezés eredménye egy megadott állandóval egyenlő-e, ahogy az alábbi példa is mutatja:

public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
{
    1 => 12.0m,
    2 => 20.0m,
    3 => 27.0m,
    4 => 32.0m,
    0 => 0.0m,
    _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
};

Állandó mintában bármilyen állandó kifejezést használhat, például:

A kifejezésnek olyan típusnak kell lennie, amely konstanstípussá konvertálható, egyetlen kivétellel: Olyan kifejezés, amelynek típusa Span<char> állandó sztringekkel egyeztethető vagy ReadOnlySpan<char> megfeleltethető.

A kereséshez nullhasználjon állandó mintát, ahogyan az az alábbi példában látható:

if (input is null)
{
    return;
}

A fordító garantálja, hogy nem hív meg felhasználó által túlterhelt egyenlőségi operátort == a kifejezés x is nullkiértékelésekor.

A nem null értékű állandók ellenőrzéséhez negated, ahogy az alábbi példa is mutatja:

if (input is not null)
{
    // ...
}

További információkért tekintse meg a funkciójavaslat megjegyzésének Állandó minta szakaszát.

Relációs minták

Relációs mintával összehasonlíthat egy kifejezés eredményét egy állandóval, ahogy az alábbi példa is mutatja:

Console.WriteLine(Classify(13));  // output: Too high
Console.WriteLine(Classify(double.NaN));  // output: Unknown
Console.WriteLine(Classify(2.4));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

Relációs mintában használja a relációs operátorok< bármelyikét, ><=vagy >=. A relációs minta jobb oldali részének állandó kifejezésnek kell lennie. Az állandó kifejezés lehet egész szám, lebegőpontos, karakter vagy szám típusú.

Ha ellenőrizni szeretné, hogy egy kifejezés eredménye egy adott tartományba tartozik-e, egy kötőhártya-mintával andkell-e egyeznie, ahogy az alábbi példa mutatja:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14)));  // output: spring
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19)));  // output: summer
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17)));  // output: winter

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    >= 3 and < 6 => "spring",
    >= 6 and < 9 => "summer",
    >= 9 and < 12 => "autumn",
    12 or (>= 1 and < 3) => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};

Ha egy kifejezés eredménye null null értékű vagy nemboxolásos átalakítással konstans típusra konvertálható, akkor a relációs minta nem egyezik meg a kifejezéssel.

További információ: A C# nyelvi specifikációjának Relációs minták szakasza.

Logikai minták

notA , andés or minta kombinátorokkal hozza létre a következő logikai mintákat:

  • Olyan tagadásinot minta, amely megfelel egy kifejezésnek, ha a negated minta nem egyezik meg a kifejezéssel. Az alábbi példa bemutatja, hogyan tagadhat meg egy állandónull mintát annak ellenőrzéséhez, hogy egy kifejezés nem null értékű-e:

    if (input is not null)
    {
        // ...
    }
    
  • Kötőjelesand minta, amely akkor felel meg egy kifejezésnek, ha mindkét minta megegyezik a kifejezéssel. Az alábbi példa bemutatja, hogyan kombinálhatja a relációs mintákat annak ellenőrzéséhez, hogy egy érték egy adott tartományban van-e:

    Console.WriteLine(Classify(13));  // output: High
    Console.WriteLine(Classify(-100));  // output: Too low
    Console.WriteLine(Classify(5.7));  // output: Acceptable
    
    static string Classify(double measurement) => measurement switch
    {
        < -40.0 => "Too low",
        >= -40.0 and < 0 => "Low",
        >= 0 and < 10.0 => "Acceptable",
        >= 10.0 and < 20.0 => "High",
        >= 20.0 => "Too high",
        double.NaN => "Unknown",
    };
    
  • Disjunctiveor pattern, amely megfelel egy kifejezésnek, ha bármelyik minta megegyezik a kifejezéssel, ahogy az alábbi példa mutatja:

    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring
    
    static string GetCalendarSeason(DateTime date) => date.Month switch
    {
        3 or 4 or 5 => "spring",
        6 or 7 or 8 => "summer",
        9 or 10 or 11 => "autumn",
        12 or 1 or 2 => "winter",
        _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
    };
    

Ahogy az előző példa is mutatja, a mintakombinátorokat többször is használhatja egy mintában.

Az ellenőrzés sorrendje és sorrendje

A mintakombinátorok ebben a sorrendben ellenőrzik a kifejezéseket a kifejezések kötési sorrendje alapján:

  • not
  • and
  • or

A not minta először az operandusához kötődik. A and minta minden not mintakifejezés kötése után megköt. A or minta végül not is kötődik, és and a minták az operandusokhoz kötődnek. Az alábbi példa az összes olyan karaktert megpróbálja egyezni, amely nem kisbetű. az Hiba történt, mert a not minta a minta előtt and kötődik:

// Incorrect pattern. `not` binds before `and`
static bool IsNotLowerCaseLetter(char c) => c is not >= 'a' and <= 'z';

Az alapértelmezett kötés azt jelenti, hogy az előző példa a következő példaként van elemezve:

// The default binding without parentheses is shows in this method. `not` binds before `and`
static bool IsNotLowerCaseLetterDefaultBinding(char c) => c is ((not >= 'a') and <= 'z');

A hiba kijavításához adja meg, hogy a not minta a kifejezéshez kötődjön >= 'a' and <= 'z' :

// Correct pattern. Force `and` before `not`
static bool IsNotLowerCaseLetterParentheses(char c) => c is not (>= 'a' and <= 'z');

A zárójelek hozzáadása egyre fontosabbá válik, mivel a minták egyre bonyolultabbá válnak. Általában zárójelekkel egyértelműsítse a mintákat más fejlesztők számára, ahogy az alábbi példa is mutatja:

static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

Feljegyzés

Az a sorrend, amelyben a fordító ellenőrzi az azonos kötési sorrendű mintákat, nincs meghatározva. Futásidőben a fordító először több minta és több orand minta jobb oldali beágyazott mintáit ellenőrizheti.

További információ: A C# nyelv specifikációjának Mintakombinátorok szakasza.

Tulajdonságminta

Egy tulajdonságmintával egyezhet egy kifejezés tulajdonságaival vagy mezőivel a beágyazott mintákhoz, ahogy az alábbi példa is mutatja:

static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };

A tulajdonságminta akkor egyezik meg egy kifejezéssel, ha a kifejezés eredménye nem null, és minden beágyazott minta megegyezik a kifejezés eredményének megfelelő tulajdonságával vagy mezőjével.

Egy tulajdonságmintához megadhat egy futásidejű típusellenőrzést és egy változódeklarációt, ahogy az alábbi példa is mutatja:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));  // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,

    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),

    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

Ez a szerkezet kifejezetten azt jelenti, hogy az üres tulajdonságminta is { } minden nem null értékű tulajdonságnak felel meg, és ahelyett is not null , hogy egy változót hoz létre: somethingPossiblyNull is { } somethingDefinitelyNotNull.

if (GetSomeNullableStringValue() is { } nonNullValue) // Empty property pattern with variable creation
{
    Console.WriteLine("NotNull:" + nonNullValue);
}
else
{
    nonNullValue = "NullFallback"; // we can access the variable here.
    Console.WriteLine("it was null, here's the fallback: " + nonNullValue);
}

A tulajdonságminta rekurzív minta. Bármilyen mintát használhat beágyazott mintaként. Tulajdonságmintával egyezhet az adatok egyes részeivel a beágyazott mintákkal, ahogy az alábbi példa is mutatja:

public record Point(int X, int Y);
public record Segment(Point Start, Point End);

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };

Az előző példa a minta-kombinátort és a orrekordtípusokat használja.

Beágyazott tulajdonságokra vagy mezőkre hivatkozhat egy tulajdonságmintán belül. Ezt a képességet kiterjesztett tulajdonságmintának nevezzük. Az előző példában szereplő metódust például újrafunkálhatja a következő egyenértékű kódba:

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start.Y: 0 } or { End.Y: 0 };

További információ: A C# szabvány Tulajdonságminta szakasza.

Tipp.

A kód olvashatóságának javítása érdekében használja a Tulajdonságminta egyszerűsítése (IDE0170) stílusszabályt. Kiterjesztett tulajdonságminták használatát javasolja.

Pozícióminta

Pozíciómintával dekonstruálhat egy kifejezést, és megfeleltetheti az eredményként kapott értékeket a megfelelő beágyazott mintáknak, ahogy az alábbi példa mutatja:

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

Az előző példában egy kifejezés típusa tartalmazza a Deconstruct metódust, amelyet a minta egy kifejezés eredményének dekonstruálásához használ.

Fontos

A pozíciómintában szereplő tagok sorrendjének meg kell egyeznie a Deconstruct metódus paramétereinek sorrendjével. A pozíciómintához létrehozott kód meghívja a metódust Deconstruct .

A rekordtípusok kifejezéseit a pozíciómintákhoz is igazíthatja. Ezzel a módszerrel több bemenetet is egyeztethet különböző mintákkal, ahogy az alábbi példa is mutatja:

static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
    => (groupSize, visitDate.DayOfWeek) switch
    {
        (<= 0, _) => throw new ArgumentException("Group size must be positive."),
        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
        (>= 10, DayOfWeek.Monday) => 30.0m,
        (>= 5 and < 10, _) => 12.0m,
        (>= 10, _) => 15.0m,
        _ => 0.0m,
    };

Az előző példa relációs és logikai mintákat használ.

A rekordelemek és Deconstruct paraméterek neveit egy pozíciómintában használhatja, ahogy az alábbi példa is mutatja:

var numbers = new List<int> { 1, 2, 3 };
if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");  // output: Sum of [1 2 3] is 6
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

A pozíciómintát az alábbi módokon is kiterjesztheti:

  • Adjon hozzá egy futásidejű típusellenőrzést és egy változódeklarációt az alábbi példában látható módon:

    public record Point2D(int X, int Y);
    public record Point3D(int X, int Y, int Z);
    
    static string PrintIfAllCoordinatesArePositive(object point) => point switch
    {
        Point2D (> 0, > 0) p => p.ToString(),
        Point3D (> 0, > 0, > 0) p => p.ToString(),
        _ => string.Empty,
    };
    

    Az előző példa olyan pozíciórekordokat használ, amelyek implicit módon biztosítják a metódustDeconstruct.

  • Használjon tulajdonságmintát egy pozíciómintán belül, ahogyan az az alábbi példában látható:

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Kombinálja az előző két használatot az alábbi példában látható módon:

    if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p)
    {
        // ..
    }
    

A pozícióminta rekurzív minta. Ez azt is jelentheti, hogy bármilyen mintát használhat beágyazott mintaként.

További információkért tekintse meg a funkciójavaslat megjegyzésének Pozícióminta szakaszát.

var minta

Használjon egyvar mintát bármely kifejezéshez, beleértve nullaz eredményt is, és rendelje hozzá az eredményt egy új helyi változóhoz, ahogy az alábbi példa mutatja:

static bool IsAcceptable(int id, int absLimit) =>
    SimulateDataFetch(id) is var results 
    && results.Min() >= -absLimit 
    && results.Max() <= absLimit;

static int[] SimulateDataFetch(int id)
{
    var rand = new Random();
    return Enumerable
               .Range(start: 0, count: 5)
               .Select(s => rand.Next(minValue: -10, maxValue: 11))
               .ToArray();
}

A var minta akkor hasznos, ha ideiglenes változóra van szüksége egy logikai kifejezésben a köztes számítások eredményének tárolásához. Akkor is használhat var mintát, ha több ellenőrzést when kell végrehajtania egy switch kifejezés vagy utasítás őrei esetében, ahogy az alábbi példa mutatja:

public record Point(int X, int Y);

static Point Transform(Point point) => point switch
{
    var (x, y) when x < y => new Point(-x, y),
    var (x, y) when x > y => new Point(x, -y),
    var (x, y) => new Point(x, y),
};

static void TestTransform()
{
    Console.WriteLine(Transform(new Point(1, 2)));  // output: Point { X = -1, Y = 2 }
    Console.WriteLine(Transform(new Point(5, 2)));  // output: Point { X = 5, Y = -2 }
}

Az előző példában a minta var (x, y) egy pozíciómintával (var x, var y)egyenértékű.

var A mintában a deklarált változó típusa annak a kifejezésnek a fordítási idő típusa, amelyet a minta egyezik.

További információkért tekintse meg a funkciójavaslat megjegyzésének Var minta szakaszát.

Minta elvetése

Használjon elvetési mintát_ bármely kifejezéshez, például nullaz alábbi példában látható módon:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));  // output: 5.0
Console.WriteLine(GetDiscountInPercent(null));  // output: 0.0
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));  // output: 0.0

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

Az előző példában egy elvetési minta fogópontjai null és bármely olyan egész számérték, amely nem rendelkezik az DayOfWeek enumerálás megfelelő tagjával. Ez biztosítja, hogy a switch példában szereplő kifejezés kezelje az összes lehetséges bemeneti értéket. Ha nem használ elvetési mintát egy switch kifejezésben, és a kifejezés egyik mintája sem egyezik meg egy bemenettel, a futtatókörnyezet kivételt jelez. A fordító figyelmeztetést generál, ha egy switch kifejezés nem kezeli az összes lehetséges bemeneti értéket.

Az elvetési minta nem lehet minta egy is kifejezésben vagy utasításban switch . Ezekben az esetekben a kifejezések egyeztetéséhez használjon egy var mintát egy elvetéssel: var _. Az elvetési minta egy kifejezés mintája switch lehet.

További információ: A funkciójavaslat megjegyzésének Minta elvetése szakasza.

Zárójeles minta

Zárójeleket bármilyen minta köré helyezhet. Ez általában a logikai minták elsőbbségének hangsúlyozására vagy módosítására szolgál, ahogy az alábbi példa is mutatja:

if (input is not (float or double))
{
    return;
}

Listaminták

Egy tömböt vagy listát egy mintasorozattal egyezhet meg, ahogy az alábbi példa is mutatja:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

Ahogy az előző példa is mutatja, a listaminta akkor egyezik, ha minden beágyazott minta egyezik egy bemeneti sorozat megfelelő elemével. A listamintán belül bármilyen mintát használhat. Bármely elemnek megfeleltetheti az elvetési mintát , vagy ha az elemet is rögzíteni szeretné, használja a var mintát, ahogyan az alábbi példa mutatja:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

Az előző példák egy teljes bemeneti sorozatot egy listamintával egyeznek meg. Ha csak egy bemeneti sorozat elején vagy végén – vagy mindkettőben – szeretné egyezni az elemeket, használja a szeletmintát.., ahogyan az az alábbi példában látható:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

A szeletminta nulla vagy több elemnek felel meg. A listamintában legfeljebb egy szeletmintát használhat. A szeletminta csak listamintában jelenhet meg.

Egy alpattert egy szeletmintán belül is beágyazhat, ahogy az alábbi példa is mutatja:

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

További információ: Listaminta a C#-nyelv specifikációjában.

Zárt hierarchiaminták

A C# 15-től kezdődően egy switch olyan kifejezés, amelynek szabályozási típusa egy closed osztály, kimerítő , ha a karjai az adott osztály minden közvetlen leszármazottját kezelik. A fordító nem igényel alapértelmezett kart, mert a kapcsoló teljes körű:

public closed record class PaymentMethod;
public record class Cash : PaymentMethod;
public record class Card(string Last4) : PaymentMethod;
public record class BankTransfer(string Iban) : PaymentMethod;
public static string Describe(PaymentMethod method) => method switch
{
    Cash => "cash",
    Card(var last4) => $"card ending {last4}",
    BankTransfer(var iban) => $"bank transfer to {iban}",
    // No warning: every direct descendant of 'PaymentMethod' is handled.
};

A zárt hierarchiakapcsoló csak akkor teljes körű, ha minden közvetlen leszármazott elérhető a kapcsoló helyéről. Ha egy közvetlen leszármazott kevésbé érhető el, mint a zárt alaptípus, és nem látható a kapcsolóhelyen, a fordító kezeletlenként kezeli, és figyelmezteti, hogy a kapcsoló nem teljes.

Egy zárt public alaposztálynak például közvetlen leszármazottja lehet internal . Az ugyanabban a szerelvényben lévő kód a leszármazottak teljes készletét látja, de egy másik szerelvény kódja nem:

// Assembly 1
public closed record class Shape;
public record class Circle(double Radius) : Shape;
internal record class Triangle(double Base, double Height) : Shape;
// Same assembly: 'Triangle' is visible, so the switch is exhaustive.
internal static double Area(Shape shape) => shape switch
{
    Circle(var r) => Math.PI * r * r,
    Triangle(var b, var h) => 0.5 * b * h,
};
// Assembly 2
public static string Name(Shape shape) => shape switch
{
    Circle => "circle",
    // Warning CS8509: the switch expression doesn't handle all possible values.
    // 'Triangle' is a direct descendant of 'Shape' but isn't visible here.
};

A 2. szerelvény teljességének visszaállításához adjon hozzá egy eldobott kart (_ => ...), adjon hozzá egy alaposztálykart (Shape az előző példában), vagy minden közvetlen leszármazottat legalább olyan akadálymentessé tegyen, mint a zárt alaptípus.

Ha a szabályozási típus null értékű, null a kapcsolónak egy további értéket kell kezelnie. A kart kihagyó null váltás PaymentMethod? még akkor sem teljes, ha minden közvetlen leszármazott egyezik.

A zárt osztályból származó származtatás nem tranzitív: a zárt osztály nem zárt leszármazottja más szerelvényekből származtatható. A fordító csak a közvetlen leszármazottakat kezeli teljes készletként. A leszármazóra való váltáshoz a teljesség ellenőrzése is előnyös, deklarálja a leszármazót closed (vagy sealed).

Mivel a közvetett leszármazottak nem bővítik a zárt bázis teljes készletét, nem kell minden tranzitív altípushoz kart hozzáadni a teljesség kielégítése érdekében. A karok közötti altevékenység ugyanúgy működik, mint bármely osztályhierarchia esetében: az alaptípusnak megfelelő karok minden altípust lefednek, és az egyik altípusnak megfelelő későbbi kar nem érhető el. Vegyünk egy zárt Vehicle , amelynek közvetlen leszármazottai, Car valamint Truckegy közvetett leszármazott Sedan deklarált egy másik közgyűlés:

// Assembly 1
public closed record class Vehicle;
public record class Car(int Doors) : Vehicle;
public record class Truck(double PayloadTons) : Vehicle;

// Assembly 2
public record class Sedan(int Doors) : Car(Doors);

A váltás Vehicle a kezelés Car után teljes körű, és Truckmég akkor is, ha Sedan létezik. A Car kar minden Sedan értéket lefed:

public static string Category(Vehicle v) => v switch
{
    Car => "car",
    Truck => "truck",
    // No warning. The 'Car' arm covers 'Sedan' through ordinary subtype matching.
};

A konkrét szállításhoz Sedan helyezze a karját a Car kar elé. A Car kar továbbra is elérhető marad, mert továbbra is megfelel minden Car olyannak, amely nem a Sedankövetkező:

public static string CategorySedanFirst(Vehicle v) => v switch
{
    Sedan => "sedan",
    Car => "car",      // Reachable: 'Car' values that aren't 'Sedan'.
    Truck => "truck",
};

A két kar megfordítása alkonyati hibát okoz, ugyanúgy, mint bármely más osztályhierarchiában. A fordító észleli, hogy a Car kar már kiterjed Sedan:

string Category(Vehicle v) => v switch
{
    Car => "car",
    Sedan => "sedan",  // Error CS8510: the pattern is unreachable. It has already been handled by 'Car => ...'.
    Truck => "truck",
};

Ha azt szeretné, hogy a teljesség kövesd tovább a hierarchiát, deklaráld Car saját magát closed. A fordító ezután a (például Sedan) minden közvetlen leszármazottját Car a teljes készletként kezeli, amely a következő helyen Cargyökerezik: . Olyan kapcsoló, amelynek szabályozási típusa Car teljes, ha az alábbiak bármelyikét elvégzi:

  • Minden közvetlen leszármazottat Car saját karjával kezel.
  • Tartalmaz egy kart Car , amely minden Car értéket lefed (az összes altípust is beleértve).
  • Tartalmaz egy eldobott kart (_ => ...) vagy egy kart egy alaptípushoz Car, például Vehicle.

A kapcsoló, amelynek szabályozási típusa Vehicle választási lehetőségeket tartalmaz: Kód, amely kezeli Car és Truck továbbra is teljes, mert a kar lefedi a Car minden altípusát Car. A jelöléssel Carclosed egyszerűen megadhat egy második lehetőséget a kapcsolóhoz. Megtarthatja az egyetlen kart Car , vagy lecserélheti egy karra a közvetlen leszármazottjanként Car (a Truck kar mellett), és továbbra is teljes lehet.

A megjelölés Carclosed implicit módon abstractis lehetővé teszi, ami azt jelenti, hogy már nem hozhat létre közvetlen példányokat Car . Ez lehet, hogy nem felel meg a tervnek. Ha példányosíthatónak kell Car maradnia, hagyja nyitva, és a korábban bemutatott karok megrendelésével küldje el a kívánt altípusokat.

Típusparaméter típusok szabályozása

Az switch a kifejezés, amelynek szabályozási típusa egy zárt osztályra korlátozott típusparaméter, a teljesség igénye ugyanaz, mint a zárt osztályra való váltás. Ha az általános kódból egy zárt hierarchiát szeretne küldeni, korlátozza a típusparamétert a zárt bázisra, és kezelje az összes közvetlen leszármazottat:

public static string Describe<X>(X method) where X : PaymentMethod => method switch
{
    Cash => "cash",
    Card(var last4) => $"card ending {last4}",
    BankTransfer(var iban) => $"bank transfer to {iban}",
    // No warning: 'X' is constrained to the closed type 'PaymentMethod',
    // so handling every direct descendant exhausts the switch.
};

További információ: zárt módosító. A specifikációt lásd: Zárt hierarchiák.

Egyesítő minták

A C# 15-től kezdődően, amikor egy minta bejövő értéke egyesítő típusú, a minták automatikusan feloldják az egyesítést. Ezek az unió tulajdonára Value vonatkoznak, nem pedig magára az unió értékére. Ez a viselkedés transzparenssé teszi az egyesítést a mintaegyeztetés felé:

public record class Cat(string Name);
public record class Dog(string Name);
public union Pet(Cat, Dog);

string Describe(Pet pet) => pet switch
{
    Dog d => d.Name,
    Cat c => c.Name,
};

Két minta kivétel: a var minta és az elvetési _ minta az egyesítő értékre vonatkozik, nem pedig a tulajdonságára Value .

A null minta ellenőrzi, hogy az egyesítő Value null értékű-e. Az osztályalapú egyesítések esetében akkor is sikeres, null ha maga az egyesítő hivatkozás null értékű.

Ha egy egyesítő típus biztosítja a nem boxing hozzáférési mintát (HasValue és TryGetValue tagokat), a fordító ezeket a tagokat használja, hogy elkerülje az érték típusú boxolást a mintaegyezés során.

További információ: Unió egyeztetése. A specifikációt lásd: Unions.

C# nyelvspecifikáció

További információkért tekintse meg a C# nyelvspecifikációmintáit és mintaegyeztetési szakaszát, beleértve a következőket:

Lásd még