Musterabgleich: Die Ausdrücke is und switch und die Operatoren and, or und not in Mustern

Sie verwenden den is-Ausdruck, die switch-Anweisung und den switch-Ausdruck, um einem Eingabeausdruck einer beliebigen Anzahl von Merkmalen zuzuordnen. C# unterstützt mehrere Muster, einschließlich „Typ“, „Konstante“, „relational“, „Eigenschaft“, „Liste“, „var“ und „Ausschuss“. Muster können mit den booleschen Logikschlüsselwörtern and, or und not kombiniert werden.

Die folgenden C#-Ausdrücke und -Anweisungen unterstützen Musterabgleiche:

In diesen Konstrukten können Sie einen Eingabeausdruck gegen jedes der folgenden Muster abgleichen:

  • Deklarationsmuster: um den Laufzeittyp eines Ausdrucks zu überprüfen und bei einem erfolgreichen Abgleich einer deklarierten Variable ein Ausdrucksergebnis zuzuweisen.
  • Typmuster: um den Laufzeittyp eines Ausdrucks zu überprüfen.
  • Konstantenmuster: um zu testen, ob ein Ausdrucksergebnis einer angegebenen Konstante entspricht.
  • Relationale Muster: um ein Ausdrucksergebnis mit einer angegebenen Konstante zu vergleichen.
  • Logische Muster: um zu testen, ob ein Ausdruck mit einer logischen Kombination von Mustern übereinstimmt.
  • Eigenschaftsmuster: um zu testen, ob die Eigenschaften oder Felder eines Ausdrucks mit geschachtelten Mustern übereinstimmen.
  • Positionsmuster: um ein Ausdrucksergebnis zu dekonstruieren und zu testen, ob die resultierenden Werte mit geschachtelten Mustern übereinstimmen.
  • var-Muster: um einen beliebigen Ausdruck abzugleichen und dessen Ergebnis einer deklarierten Variablen zuzuweisen.
  • Ausschussmuster: um einen beliebigen Ausdruck abzugleichen.
  • Listenmuster: Um zu testen, ob Sequenzelemente den entsprechenden geschachtelten Mustern entsprechen. Eingeführt in C# 11.

Die Muster logisch, Eigenschaft, positionell und Liste sind rekursive Muster. Das heißt, Sie können geschachtelte Muster enthalten.

Ein Beispiel dazu, wie diese Muster verwendet werden, um einen datengesteuerten Algorithmus zu erstellen, finden Sie unter Tutorial: Verwenden von Musterabgleich, um typgesteuerte und datengesteuerte Algorithmen zu erstellen.

Deklarations- und Typmuster

Sie können Deklarations- und Typmuster verwenden, um zu prüfen, ob der Laufzeittyp eines Ausdrucks mit einem angegebenen Typ kompatibel ist. Mit einem Deklarationsmuster können Sie auch eine neue lokale Variable deklarieren. Wenn ein Deklarationsmuster mit einem Ausdruck übereinstimmt, wird dieser Variablen ein konvertiertes Ausdrucksergebnis zugewiesen, wie im folgenden Beispiel gezeigt:

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

Ein Deklarationsmuster mit dem Typ T entspricht einem Ausdruck, wenn ein Ausdrucksergebnis nicht NULL ist und eine der folgenden Bedingungen zutrifft:

  • Der Laufzeittyp eines Ausdrucksergebnisses ist T.

  • Der Laufzeittyp eines Ausdrucksergebnisses wird vom Typ T abgeleitet, oder er implementiert die T-Schnittstelle, oder es gibt eine andere implizite Verweiskonvertierung von diesem Typ zu T. Das folgende Beispiel zeigt zwei Fälle, in denen diese Bedingung whr ist:

    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,
    };
    

    Im vorangehenden Beispiel entspricht das erste Muster im ersten Aufruf der GetSourceLabel-Methode einem Argumentwert, da der Laufzeittyp int[] des Arguments vom Typ Array abgeleitet wird. Im zweiten Aufruf der GetSourceLabel-Methode wird der Laufzeittyp List<T> des Arguments nicht vom Typ Array abgeleitet, er implementiert aber die ICollection<T>-Schnittstelle.

  • Der Laufzeittyp eines Ausdrucksergebnisses ist ein Nullwerte zulassender Typ mit dem zugrunde liegenden Typ T.

  • Eine Boxing- oder Unboxing-Konvertierung ist vom Laufzeittyp eines Ausdrucksergebnisses bis zum Typ T vorhanden.

Im folgenden Beispiel werden die beiden letzten Bedingungen veranschaulicht:

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

Wenn Sie nur den Typ eines Ausdrucks überprüfen möchten, können Sie eine Ausschussvariable _ anstelle des Namens einer Variablen verwenden, wie im folgenden Beispiel gezeigt:

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)),
    };
}

Sie können zu diesem Zweck ein Typmusterverwenden, wie im folgenden Beispiel gezeigt:

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)),
};

Wie bei einem Deklarationsmuster stimmt ein Typmuster mit einem Ausdruck überein, wenn ein Ausdrucksergebnis nicht NULL ist und sein Laufzeittyp eine der oben aufgeführten Bedingungen erfüllt.

Um nach Werten ungleich NULL zu suchen, können Sie ein negiertesnull-Konstantenmuster wie im folgenden Beispiel verwenden.

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

Weitere Informationen finden Sie in den Abschnitten Deklarationsmuster und Typmuster der Hinweise zum Featurevorschlag.

Konstantenmuster

Sie verwenden ein Konstantenmuster, um zu testen, ob ein Ausdrucksergebnis einer angegebenen Konstante entspricht, wie im folgenden Beispiel gezeigt:

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)),
};

In einem Konstantenmuster können Sie einen beliebigen konstanten Ausdruck verwenden, z. B.:

  • ein numerisches Literal mit integralem oder Gleitkomma-Typ
  • Ein char
  • Ein string-Literal (Zeichenfolgenliteral)
  • ein boolescher Wert true oder false
  • ein enum-Wert
  • der Name eines deklarierten const-Felds oder lokalen const-Ausdrucks
  • null

Der Ausdruck muss ein Typ sein, der in den Konstantentyp konvertiert werden kann, mit einer Ausnahme: Ein Ausdruck, dessen Typ Span<char> oder ReadOnlySpan<char> ist und mit konstanten Zeichenfolgen in C# 11 und höheren Versionen abgeglichen werden kann.

Verwenden Sie ein Konstantenmuster, um auf null zu prüfen, wie im folgenden Beispiel gezeigt:

if (input is null)
{
    return;
}

Der Compiler stellt sicher, dass kein vom Benutzer überladener Gleichheitsoperator == aufgerufen wird, wenn der Ausdruck x is null ausgewertet wird.

Sie können ein negiertesnull-Konstantenmuster verwenden, um auf nicht NULL zu prüfen, wie im folgenden Beispiel gezeigt:

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

Weitere Informationen finden Sie im Abschnitt Konstantenmuster des Hinweises zum Featurevorschlag.

Relationale Muster

Sie verwenden ein relationales Muster, um ein Ausdrucksergebnis mit einer Konstante zu vergleichen, wie im folgenden Beispiel gezeigt:

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",
};

In einem relationalen Muster können Sie jeden der relationalen Operatoren<, >, <= oder >= verwenden. Der rechte Teil eines relationalen Musters muss ein konstanter Ausdruck sein. Der Konstantenausdruck kann einen der integralen, Gleitkomma-, char- oder enum-Typen haben.

Um zu überprüfen, ob sich ein Ausdrucksergebnis in einem bestimmten Bereich befindet, gleichen Sie es mit einem konjunktiven and-Muster ab, wie im folgenden Beispiel gezeigt:

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}."),
};

Wenn ein Ausdrucksergebnis gleich null ist oder in einer Nullwerte zulassenden oder Unboxing-Konvertierung nicht in den Typ einer Konstante konvertiert werden kann, entspricht ein relationales Muster keinem Ausdruck.

Weitere Informationen finden Sie im Abschnitt Relationale Muster des Hinweises zum Featurevorschlag.

Logische Muster

Sie verwenden die not-, and- und or-Musterkombinatoren, um die folgenden logischen Muster zu erstellen:

  • Negations-not-Muster, das mit einem Ausdruck übereinstimmt, wenn das negierte Muster nicht mit dem Ausdruck übereinstimmt. Das folgende Beispiel zeigt, wie Sie ein konstantesnull-Muster negieren können, um zu überprüfen, ob ein Ausdruck ungleich NULL ist:

    if (input is not null)
    {
        // ...
    }
    
  • Konjunktivesand-Muster, das mit einem Ausdruck übereinstimmt, wenn beide Muster mit dem Ausdruck übereinstimmen. Im folgenden Beispiel wird gezeigt, wie Sie relationale Muster kombinieren können, um zu überprüfen, ob ein Wert in einem bestimmten Bereich liegt:

    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",
    };
    
  • Disjunktivesor-Muster, das mit einem Ausdruck übereinstimmt, wenn eines der Muster mit dem Ausdruck übereinstimmt, wie im folgenden Beispiel gezeigt:

    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}."),
    };
    

Wie das vorherige Beispiel zeigt, können Sie die Musterkombinatoren wiederholt in einem Muster verwenden.

Vorrang und Auswertungsreihenfolge

Die Musterkombinatoren werden wie folgt von der höchsten Rangfolge bis zur niedrigsten sortiert:

  • not
  • and
  • or

Wenn ein logisches Muster ein Muster eines is Ausdrucks ist, ist die Rangfolge logischer Musterkombinatoren höher als die Rangfolge logischer Operatoren (sowohl bitweise logische als auch boolesche logische Operatoren). Andernfalls ist die Rangfolge logischer Musterkombinatoren niedriger als die Rangfolge logischer und bedingter logischer Operatoren. Die vollständige Liste der nach Rangfolgenebene sortierten C#-Operatoren finden Sie im Abschnitt Operatorrangfolge im Artikel C#-Operatoren.

Verwenden Sie Klammern, um die Rangfolge explizit anzugeben, wie im folgenden Beispiel gezeigt:

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

Hinweis

Die Reihenfolge, in der Muster überprüft werden, ist nicht definiert. Zur Laufzeit können die rechten geschachtelten Muster von or- und and-Mustern zuerst geprüft werden.

Weitere Informationen finden Sie im Abschnitt Musterkombinatoren des Hinweises zum Featurevorschlag.

Eigenschaftsmuster

Sie verwenden ein Eigenschaftsmuster, um die Eigenschaften oder Felder eines Ausdrucks mit den geschachtelten Mustern abzugleichen, wie im folgenden Beispiel gezeigt:

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

Ein Eigenschaftsmuster stimmt mit einem Ausdruck überein, wenn ein Ausdrucksergebnis nicht NULL ist und jedes geschachtelte Muster mit der entsprechenden Eigenschaft oder dem entsprechenden Feld des Ausdrucksergebnisses übereinstimmt.

Sie können einem Eigenschaftsmuster auch eine Laufzeittypüberprüfung und eine Variablendeklaration hinzufügen, wie im folgenden Beispiel gezeigt:

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."),
};

Ein Eigenschaftsmuster ist ein rekursives Muster. Das heißt, Sie können ein beliebiges Muster als ein geschachteltes Muster verwenden. Verwenden Sie ein Eigenschaftsmuster, um Teile von Daten gegen geschachtelte Muster abzugleichen, wie im folgenden Beispiel gezeigt:

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 } };

Im vorherigen Beispiel werden der orMusterkombinator und Datensatztypen verwendet.

Ab C# 10 können Sie auf geschachtelte Eigenschaften oder Felder innerhalb eines Eigenschaftsmusters verweisen. Diese Funktion wird als erweitertes Eigenschaftenmuster bezeichnet. Beispielsweise können Sie die Methode aus dem vorherigen Beispiel in den folgenden äquivalenten Code umgestalten:

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

Weitere Informationen finden Sie im Abschnitt Eigenschaftenmuster des Hinweises zum Featurevorschlag sowie im Hinweis zum Featurevorschlag Muster für erweiterte Eigenschaften.

Tipp

Sie können die Stilregel Eigenschaftenmuster vereinfachen (IDE0170) verwenden, um die Lesbarkeit von Code zu verbessern, indem Sie vorschlagen, wo erweiterte Eigenschaftenmuster verwendet werden sollen.

Positionsmuster

Sie verwenden ein Positionsmuster, um ein Ausdrucksergebnis zu dekonstruieren und die resultierenden Werte mit den entsprechenden geschachtelten Muster abzugleichen, wie im folgenden Beispiel gezeigt:

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",
};

Im vorherigen Beispiel enthält der Typ eines Ausdrucks die Deconstruct-Methode, die zum Dekonstruieren eines Ausdrucksergebnisses verwendet wird. Sie können auch Ausdrücke von Tupeltypen gegen Positionsmuster abgleichen. Auf diese Weise können Sie mehrere Eingaben gegen verschiedene Muster abgleichen, wie im folgenden Beispiel gezeigt:

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,
    };

Im vorherigen Beispiel werden relationale und logische Muster verwendet.

Sie können die Namen von Tupelelementen und Deconstruct-Parametern in einem Positionsmuster verwenden, wie im folgenden Beispiel gezeigt:

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);
}

Sie können ein Positionsmuster auch auf eine der folgenden Arten erweitern:

  • Fügen Sie eine Laufzeittypüberprüfung und eine Variablendeklaration hinzu, wie im folgenden Beispiel gezeigt:

    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,
    };
    

    Im vorherigen Beispiel werden Datensätze mit Feldern fester Breite verwendet, die implizit die Deconstruct-Methode bereitstellen.

  • Verwenden Sie ein Eigenschaftsmuster in einem Positionsmuster, wie im folgenden Beispiel gezeigt:

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Kombinieren Sie zwei vorangehende Verwendungen, wie im folgenden Beispiel gezeigt:

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

Ein Positionsmuster ist ein rekursives Muster. Das heißt, Sie können ein beliebiges Muster als ein geschachteltes Muster verwenden.

Weitere Informationen finden Sie im Abschnitt Positionsmuster des Hinweises zum Featurevorschlag.

var-Muster

Sie verwenden ein var-Muster, um einen beliebigen Ausdruck, einschließlich null, abzugleichen und dessen Ergebnis einer neuen lokalen Variablen zuzuweisen, wie im folgenden Beispiel gezeigt:

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();
}

Ein var-Muster ist nützlich, wenn Sie eine temporäre Variable in einem booleschen Ausdruck benötigen, um das Ergebnis von Zwischenberechnungen zu speichern. Sie können ein var-Muster auch verwenden, wenn Sie weitere Überprüfungen in den when-Ausdrücken eines switch-Ausdrucks oder einer switch-Anweisung ausführen müssen, wie im folgenden Beispiel gezeigt:

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 }
}

Im vorherigen Beispiel ist das Muster var (x, y) gleichwertig mit einem Positionsmuster(var x, var y).

In einem var-Muster ist der Typ einer deklarierten Variablen der Kompilierzeittyp des Ausdrucks, der gegen das Muster abgeglichen wird.

Weitere Informationen finden Sie im Abschnitt Var-Muster des Hinweises zum Featurevorschlag.

Ausschussmuster

Sie verwenden ein Ausschussmuster_, um einen beliebigen Ausdruck, einschließlich null, abzugleichen, wie im folgenden Beispiel gezeigt:

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,
};

Im vorherigen Beispiel wird ein Ausschussmuster verwendet, um null und jeden ganzzahligen Wert zu verarbeiten, der nicht den entsprechenden Member der DayOfWeek-Enumeration hat. Dadurch wird sichergestellt, dass ein switch-Ausdruck im Beispiel alle möglichen Eingabewerte verarbeitet. Wenn Sie kein Ausschussmuster in einem switch-Ausdruck verwenden und keines der Muster des Ausdrucks mit einer Eingabe übereinstimmt, löst die Runtime eine Ausnahme aus. Der Compiler generiert eine Warnung, wenn ein switch-Ausdruck nicht alle möglichen Eingabewerte verarbeiten kann.

Ein Ausschussmuster kann kein Muster in einem is-Ausdruck oder in einer switch-Anweisung sein. Verwenden Sie in diesen Fällen, um einen Ausdruck abzugleichen, ein var-Muster mit einem Ausschussmuster: var _. Ein Ausschussmuster kann kein Muster in einem switch-Ausdruck sein.

Weitere Informationen finden Sie im Abschnitt Ausschussmuster des Hinweises zum Featurevorschlag.

Muster in Klammern

Sie können Klammern um ein beliebiges Muster setzen. In der Regel tun Sie dies, um die Rangfolge in logischen Mustern hervorzuheben oder zu ändern, wie im folgenden Beispiel gezeigt:

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

Listenmuster

Ab C# 11 können Sie ein Array oder eine Liste mit einer Abfolge von Mustern abgleichen, wie im folgenden Beispiel gezeigt:

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

Wie im vorherigen Beispiel gezeigt, wird ein Listenmuster abgeglichen, wenn jedes geschachtelte Muster mit dem entsprechenden Element einer Eingabesequenz abgeglichen wird. Sie können ein beliebiges Muster in einem Listenmuster verwenden. Verwenden Sie zum Abgleichen eines Elements das Ausschussmuster, oder verwenden Sie, wenn Sie das Element auch erfassen möchten, das var-Muster, wie im folgenden Beispiel gezeigt:

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.

Die vorherigen Beispiele entsprechen einer ganzen Eingabesequenz mit einem Listenmuster. Um Elemente nur am Anfang oder/und am Ende einer Eingabesequenz abzugleichen, verwenden Sie wie im folgenden Beispiel gezeigt das Slicemuster..:

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

Ein Slicemuster entspricht 0 (null) oder mehr Elementen. Sie können höchstens ein Slicemuster in einem Listenmuster verwenden. Das Slicemuster kann nur in einem Listenmuster angezeigt werden.

Sie können auch einen Teilmuster wie im folgenden Beispiel gezeigt in einem Segmentmuster verschachteln:

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

Weitere Informationen finden Sie im Hinweis zum Featurevorschlag Listenmuster.

C#-Sprachspezifikation

Weitere Informationen finden Sie im Abschnitt Muster und Musterabgleich der C#-Sprachspezifikation.

Informationen zu in C# 8 und höher eingeführten Features finden Sie in den folgenden Featurevorschlägen:

Weitere Informationen