Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dopasowywanie wzorca —
is Użyj wyrażenia, instrukcji switch i wyrażenia switch, aby dopasować wyrażenie wejściowe do dowolnej liczby cech. Język C# obsługuje wiele wzorców, w tym deklarację, typ, stałą, relacyjną, właściwość, listę, var i odrzucanie. Wzorce można łączyć przy użyciu słów kluczowych andlogiki logicznej , ori not.
Dokumentacja języka C# zawiera ostatnio wydaną wersję języka C#. Zawiera również początkową dokumentację dla funkcjonalności w publicznych wersjach testowych nadchodzącego wydania języka.
Dokumentacja identyfikuje dowolną funkcję po raz pierwszy wprowadzoną w ostatnich trzech wersjach języka lub w bieżącej publicznej wersji zapoznawczej.
Napiwek
Aby dowiedzieć się, kiedy funkcja została po raz pierwszy wprowadzona w języku C#, zapoznaj się z artykułem dotyczącym historii wersji języka C#.
Następujące wyrażenia i instrukcje języka C# obsługują dopasowywanie wzorców:
W tych konstrukcjach można dopasować wyrażenie wejściowe do dowolnego z następujących wzorców:
- Wzorzec deklaracji: sprawdź typ czasu wykonywania wyrażenia i, jeśli dopasowanie powiedzie się, przypisz wynik wyrażenia do zadeklarowanej zmiennej.
- Wzorzec typu: sprawdź typ czasu wykonywania wyrażenia.
- Wzorzec stałej: przetestuj, czy wynik wyrażenia jest równy określonej stałej.
- Wzorce relacyjne: porównaj wynik wyrażenia z określoną stałą.
- Wzorce logiczne: przetestuj, czy wyrażenie pasuje do logicznej kombinacji wzorców.
- Wzorzec właściwości: przetestuj, czy właściwości lub pola wyrażenia pasują do zagnieżdżonych wzorców.
- Wzorzec pozycyjny: dekonstrukcja wyniku wyrażenia i sprawdź, czy wynikowe wartości pasują do zagnieżdżonych wzorców.
-
varwzorzec: dopasuj dowolne wyrażenie i przypisz jego wynik do zadeklarowanej zmiennej. - Odrzuć wzorzec: dopasuj dowolne wyrażenie.
- Wzorce listy: przetestować, czy sekwencja elementów pasuje do zagnieżdżonych wzorców.
Wzorce logiczne, właściwości, pozycyjne i list to wzorce cykliczne. Oznacza to, że mogą zawierać zagnieżdżone wzorce.
Aby zapoznać się z przykładem używania tych wzorców do tworzenia algorytmu opartego na danych, zobacz Samouczek: używanie dopasowywania wzorców do tworzenia algorytmów opartych na typach i opartych na danych.
Wzorce deklaracji i typów
Użyj deklaracji i wzorców typów, aby sprawdzić, czy typ czasu wykonywania wyrażenia jest zgodny z danym typem. Korzystając ze wzorca deklaracji, można również zadeklarować nową zmienną lokalną. Gdy wzorzec deklaracji pasuje do wyrażenia, przypisuje zmienną do wyniku przekonwertowanego wyrażenia, jak pokazano w poniższym przykładzie:
object greeting = "Hello, World!";
if (greeting is string message)
{
Console.WriteLine(message.ToLower()); // output: hello, world!
}
Wzorzec deklaracji z typem T jest zgodny z wyrażeniem, gdy wynik wyrażenia ma wartość inną niż null, a każdy z następujących warunków jest spełniony:
- Typ czasu wykonywania wyniku wyrażenia ma konwersję tożsamości na
T. - Typ
Tjest typemref structi istnieje konwersja tożsamości z wyrażenia naT. - Typ czasu wykonywania wyniku wyrażenia pochodzi z typu
T, implementuje interfejsTlub inną niejawną konwersję odwołania istnieje z niego doT. Ten warunek obejmuje relacje dziedziczenia i implementacje interfejsu. W poniższym przykładzie pokazano dwa przypadki, gdy ten warunek ma wartość true:
W poprzednim przykładzie przy pierwszym wywołaniu metody pierwszy wzorzec pasuje dovar 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, };GetSourceLabelwartości argumentu, ponieważ typint[]czasu wykonywania argumentu Array pochodzi z typu . W drugim wywołaniuGetSourceLabelmetody typ List<T> czasu wykonywania argumentu nie pochodzi z Array typu, ale implementuje ICollection<T> interfejs. - Typ czasu wykonywania wyniku wyrażenia jest typem wartości dopuszczających null z typem bazowym , a
Tjest Nullable<T>.HasValue. - Konwersja opakowywania lub rozpakowywania istnieje z typu czasu wykonywania wyniku wyrażenia do typu
T, gdy wyrażenie nie jest wystąpieniemref struct.
Wzorce deklaracji nie uwzględniają konwersji zdefiniowanych przez użytkownika ani niejawnych konwersji zakresu.
W poniższym przykładzie przedstawiono dwa ostatnie warunki:
int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
Console.WriteLine(a + b); // output: 30
}
Aby sprawdzić tylko typ wyrażenia, użyj odrzucenia _ zamiast nazwy zmiennej, jak pokazano w poniższym przykładzie:
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)),
};
}
W tym celu użyj wzorca typu, jak pokazano w poniższym przykładzie:
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)),
};
Podobnie jak wzorzec deklaracji, wzorzec typu pasuje do wyrażenia, gdy wynik wyrażenia jest inny niż null, a jego typ czasu wykonywania spełnia dowolne z powyższych warunków.
Aby sprawdzić, czy nie ma wartości null, użyj negowanegonullwzorca stałej, jak pokazano w poniższym przykładzie:
if (input is not null)
{
// ...
}
Aby uzyskać więcej informacji, zobacz sekcje Wzorzec deklaracji i Wzorzec typu specyfikacji języka C#.
Wzorzec stałej
Wzorzec stałej jest składnią alternatywną dla ==, gdy prawym operandem jest stała. Użyj wzorca stałej , aby sprawdzić, czy wynik wyrażenia jest równy określonej stałej, jak pokazano w poniższym przykładzie:
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)),
};
W wzorcu stałym można użyć dowolnego wyrażenia stałego, takiego jak:
- liczba całkowita lub literał liczbowy zmiennoprzecinkowa
- znak
- literał ciągu
- wartość
truelogiczna lubfalse - wartość wyliczenia
- nazwa zadeklarowanego pola const lub lokalnego
null
Wyrażenie musi być typem, który jest konwertowany na typ stały, z jednym wyjątkiem: wyrażenie, którego typem jest Span<char> lub ReadOnlySpan<char> może być dopasowane do ciągów stałych.
Użyj wzorca stałej, aby sprawdzić , nulljak pokazano w poniższym przykładzie:
if (input is null)
{
return;
}
Kompilator gwarantuje, że nie wywołuje przeciążonego przez użytkownika operatora == równości podczas oceniania wyrażenia x is null.
Możesz użyć negowanego wzorca stałej null, aby sprawdzić, czy nie ma wartości null, jak pokazano w poniższym przykładzie:
if (input is not null)
{
// ...
}
Aby uzyskać więcej informacji, zobacz sekcję Stały wzorzec notatki dotyczącej propozycji funkcji.
Wzorce relacyjne
Użyj wzorca relacyjnego , aby porównać wynik wyrażenia z stałą, jak pokazano w poniższym przykładzie:
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",
};
W wzorcu relacyjnym użyj dowolnych operatorów< relacyjnych, >, <=lub >=. Prawa część wzorca relacyjnego musi być wyrażeniem stałym. Wyrażenie stałe może być liczbą całkowitą, zmiennoprzecinkową, znakiem lub typem wyliczeniowym .
Aby sprawdzić, czy wynik wyrażenia znajduje się w określonym zakresie, dopasuj go do and sprzężonego, jak pokazano w poniższym przykładzie:
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}."),
};
Jeśli wynik wyrażenia jest null lub nie można przekonwertować na typ stałej przy użyciu konwersji dopuszczającej wartość null lub rozpakuj, wzorzec relacyjny nie jest zgodny z wyrażeniem.
Aby uzyskać więcej informacji, zobacz sekcję Relacyjne wzorce specyfikacji języka C#.
Wzorce logiczne
notUżyj kombinatorów wzorców , andi or , aby utworzyć następujące wzorce logiczne:
Wzorzec negacji
notzgodny z wyrażeniem, gdy negowany wzorzec nie jest zgodny z wyrażeniem. W poniższym przykładzie pokazano, jak można negowaćnullstały wzorzec, aby sprawdzić, czy wyrażenie ma wartość inną niż null:if (input is not null) { // ... }Wzorzec conjunctive
and, który pasuje do wyrażenia, gdy oba wzorce pasują do wyrażenia. W poniższym przykładzie pokazano, jak połączyć wzorce relacyjne, aby sprawdzić, czy wartość znajduje się w określonym zakresie: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", };Wzorzec disjunctive
or, który pasuje do wyrażenia, gdy dowolny wzorzec pasuje do wyrażenia, jak pokazano w poniższym przykładzie: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}."), };
Jak pokazano w poprzednim przykładzie, można wielokrotnie używać kombinatorów wzorców we wzorcu.
Pierwszeństwo i kolejność sprawdzania
Kombinatory wzorców sprawdzają wyrażenia w tej kolejności na podstawie kolejności powiązań wyrażeń:
notandor
Wzorzec not wiąże się najpierw z operandem. Wzorzec and jest powiązany po każdym not powiązaniu wyrażenia wzorca. Wzorzec or wiąże się ze wszystkimi not wzorcami i and powiązanymi z operandami. Poniższy przykład próbuje dopasować wszystkie znaki, które nie są małymi literami za a pomocą ciągu z. Występuje błąd, ponieważ not wzorzec wiąże się przed wzorcem and :
// Incorrect pattern. `not` binds before `and`
static bool IsNotLowerCaseLetter(char c) => c is not >= 'a' and <= 'z';
Powiązanie domyślne oznacza, że poprzedni przykład jest analizowany jako poniższy przykład:
// 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');
Aby naprawić błąd, określ, że not wzorzec ma być powiązany z wyrażeniem >= 'a' and <= 'z' :
// Correct pattern. Force `and` before `not`
static bool IsNotLowerCaseLetterParentheses(char c) => c is not (>= 'a' and <= 'z');
Dodawanie nawiasów staje się ważniejsze, ponieważ wzorce stają się bardziej skomplikowane. Ogólnie rzecz biorąc, użyj nawiasów, aby wyjaśnić wzorce dla innych deweloperów, jak pokazano w poniższym przykładzie:
static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
Uwaga
Kolejność, w której kompilator sprawdza wzorce, które mają tę samą kolejność powiązań, jest niezdefiniowana. W czasie wykonywania kompilator może najpierw sprawdzić zagnieżdżone wzorce wielu or wzorców i wiele and wzorców.
Aby uzyskać więcej informacji, zobacz sekcję Kombinatory wzorców specyfikacji języka C#.
Wzorzec właściwości
Użyj wzorca właściwości , aby dopasować właściwości lub pola wyrażenia do zagnieżdżonych wzorców, jak pokazano w poniższym przykładzie:
static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };
Wzorzec właściwości pasuje do wyrażenia, gdy wynik wyrażenia jest inny niż null, a każdy zagnieżdżony wzorzec pasuje do odpowiedniej właściwości lub pola wyniku wyrażenia.
Możesz dodać sprawdzanie typu czasu wykonywania i deklarację zmiennej do wzorca właściwości, jak pokazano w poniższym przykładzie:
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."),
};
Ta konstrukcja oznacza w szczególności, że wzorzec is { } właściwości pasuje do wszystkiegois not null, co nie ma wartości null, i można go użyć zamiast do utworzenia zmiennej: 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);
}
Wzorzec właściwości jest wzorcem rekursywnym. Każdy wzorzec można zastosować jako wzorzec zagnieżdżony. Użyj wzorca właściwości, aby dopasować części danych do zagnieżdżonych wzorców, jak pokazano w poniższym przykładzie:
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 } };
W poprzednim przykładzie użyto kombinatora orwzorca i typów rekordów.
Można odwoływać się do zagnieżdżonych właściwości lub pól w szablonie właściwości. Ta funkcja jest znana jako wzorzec rozszerzonej właściwości. Na przykład można refaktoryzować metodę z poprzedniego przykładu do następującego równoważnego kodu:
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start.Y: 0 } or { End.Y: 0 };
Aby uzyskać więcej informacji, zobacz sekcję Wzorzec właściwości standardu języka C#.
Napiwek
Aby zwiększyć czytelność kodu, użyj reguły stylu Upraszczanie wzorca właściwości (IDE0170). Sugeruje on miejsca do używania rozszerzonych wzorców właściwości.
Wzorzec pozycyjny
Użyj wzorca pozycyjnego , aby zdekonstruować wyrażenie i dopasować wartości wynikowe do odpowiednich zagnieżdżonych wzorców, jak pokazano w poniższym przykładzie:
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",
};
W poprzednim przykładzie typ wyrażenia zawiera metodę Deconstruct , której wzorzec używa do dekonstrukcji wyniku wyrażenia.
Ważne
Kolejność elementów członkowskich we wzorcu pozycyjnym musi być zgodna z kolejnością parametrów w metodzie Deconstruct . Kod wygenerowany dla wzorca pozycyjnego wywołuje metodę Deconstruct .
Można również dopasować wyrażenia typów krotki względem wzorców pozycyjnych. Korzystając z tego podejścia, można dopasować wiele danych wejściowych do różnych wzorców, jak pokazano w poniższym przykładzie:
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,
};
W poprzednim przykładzie użyto wzorców relacyjnych i logicznych .
Nazwy elementów krotki i Deconstruct parametrów można używać w wzorcu pozycyjnym, jak pokazano w poniższym przykładzie:
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);
}
Można również rozszerzyć wzorzec pozycyjny na dowolny z następujących sposobów:
Dodaj sprawdzanie typu czasu wykonywania i deklarację zmiennej, jak pokazano w poniższym przykładzie:
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, };W poprzednim przykładzie użyto rekordów pozycyjnych, które niejawnie udostępniają metodę
Deconstruct.Użyj wzorca właściwości w ramach wzorca pozycyjnego, jak pokazano w poniższym przykładzie:
public record WeightedPoint(int X, int Y) { public double Weight { get; set; } } static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };Połącz dwa poprzednie użycia, jak pokazano w poniższym przykładzie:
if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p) { // .. }
Wzorzec pozycyjny jest wzorcem rekursywnym. Oznacza to, że można użyć dowolnego wzorca jako wzorca zagnieżdżonego.
Aby uzyskać więcej informacji, zobacz sekcję Wzorzec pozycyjny uwagi dotyczącej propozycji funkcji.
var deseń
var Użyj wzorca, aby dopasować dowolne wyrażenie, w tym null, i przypisać jego wynik do nowej zmiennej lokalnej, jak pokazano w poniższym przykładzie:
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();
}
var Wzorzec jest przydatny, gdy potrzebna jest zmienna tymczasowa w wyrażeniu logicznym do przechowywania wyniku obliczeń pośrednich. Można również użyć var wzorca, gdy trzeba wykonać więcej kontroli w when przypadku ochrony switch wyrażenia lub instrukcji, jak pokazano w poniższym przykładzie:
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 }
}
W poprzednim przykładzie wzorzec var (x, y) jest odpowiednikiem wzorca.
var We wzorcu typ zadeklarowanej zmiennej jest typem czasu kompilacji wyrażenia zgodnego ze wzorcem.
Aby uzyskać więcej informacji, zobacz sekcję Var pattern (Wzorzec wariancji ) notatki dotyczącej propozycji funkcji.
Odrzuć wzorzec
Użyj wzorca_ odrzucenia, aby dopasować dowolne wyrażenie, w tym null, jak pokazano w poniższym przykładzie:
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,
};
W poprzednim przykładzie wzorzec odrzucania null obsługuje i dowolną wartość całkowitą, która nie ma odpowiedniego DayOfWeek elementu członkowskiego wyliczenia. Gwarantuje to, że switch wyrażenie w przykładzie obsługuje wszystkie możliwe wartości wejściowe. Jeśli nie używasz wzorca odrzucenia w wyrażeniu switch i żaden z wzorców wyrażenia nie pasuje do danych wejściowych, środowisko uruchomieniowe zgłasza wyjątek. Kompilator generuje ostrzeżenie, jeśli switch wyrażenie nie obsługuje wszystkich możliwych wartości wejściowych.
Wzorzec odrzucenia nie może być wzorcem w wyrażeniu is ani instrukcji switch . W takich przypadkach, aby dopasować dowolne wyrażenie, użyj var wzorca z odrzuceniem: var _. Wzorzec odrzucenia może być wzorcem w wyrażeniu switch .
Aby uzyskać więcej informacji, zobacz sekcję Odrzuć wzorzec notatki dotyczącej propozycji funkcji.
Wzorzec nawiasów
Nawiasy można umieścić wokół dowolnego wzorca. Zazwyczaj można to zrobić, aby podkreślić lub zmienić pierwszeństwo we wzorcach logicznych, jak pokazano w poniższym przykładzie:
if (input is not (float or double))
{
return;
}
Wzorce listy
Można dopasować tablicę lub listę do sekwencji wzorców, jak pokazano w poniższym przykładzie:
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
Jak pokazano w poprzednim przykładzie, wzorzec listy jest zgodny, gdy każdy zagnieżdżony wzorzec pasuje do odpowiedniego elementu sekwencji danych wejściowych. W ramach wzorca listy można użyć dowolnego wzorca. Aby dopasować dowolny element, użyj wzorca odrzucenia lub, jeśli chcesz również przechwycić element, użyj wzorca var, jak pokazano w poniższym przykładzie:
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.
Powyższe przykłady pasują do całej sekwencji danych wejściowych względem wzorca listy. Aby dopasować elementy tylko na początku lub końcu sekwencji danych wejściowych, użyj wzorca .., jak pokazano w poniższym przykładzie:
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
Wzorzec wycinka pasuje do zera lub większej liczby elementów. W wzorcu listy można użyć co najwyżej jednego wzorca wycinka. Wzorzec wycinka może być wyświetlany tylko we wzorcu listy.
Można również zagnieżdżać podwzorca we wzorcu wycinka, jak pokazano w poniższym przykładzie:
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
Aby uzyskać więcej informacji, zobacz Wzorzec listy w specyfikacji języka C#.
Zamknięte wzorce hierarchii
Począwszy od języka C# 15, wyrażenie, którego typ zarządzający jest klasąclosed, jest wyczerpujące, switch gdy jego ramiona obsługują każdy bezpośredni element potomny tej klasy. Kompilator nie wymaga domyślnego ramienia, ponieważ przełącznik jest wyczerpujący:
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.
};
Zamknięty przełącznik hierarchii jest wyczerpujący tylko wtedy, gdy każdy bezpośredni element potomny jest osiągalny z lokalizacji przełącznika. Jeśli bezpośredni element potomny jest mniej dostępny niż zamknięty typ podstawowy i nie jest widoczny w lokacji przełącznika, kompilator traktuje go jako nieobsługiwany i ostrzega, że przełącznik nie jest wyczerpujący.
Na przykład zamknięta public klasa bazowa może mieć bezpośredni element potomny internal . Kod w tym samym zestawie widzi pełny zestaw elementów potomnych, ale kod w innym zestawie nie:
// 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.
};
Aby przywrócić wyczerpującość zestawu 2, dodaj ramię odrzucania (_ => ...), dodaj ramię klasy bazowej (Shape w poprzednim przykładzie) lub ustaw każdy element potomny co najmniej tak dostępny, jak zamknięty typ podstawowy.
Gdy typ zarządzający jest dopuszczany do wartości null, null jest dodatkową wartością, która musi obsługiwać przełącznik. Przełącznik pomija PaymentMethod?null ramię nie jest wyczerpujące nawet wtedy, gdy każdy bezpośredni element potomny jest dopasowany.
Wyprowadzanie z zamkniętej klasy nie jest przechodnie: element potomny klasy zamkniętej może pochodzić z innych zestawów. Kompilator traktuje tylko bezpośrednie elementy potomne jako wyczerpujący zestaw. Aby przełączyć element potomny, skorzystaj również z sprawdzania wyczerpującości, zadeklaruj element potomny closed (lub sealed).
Ponieważ obiekty potomne pośrednie nie rozszerzają wyczerpującego zestawu zamkniętej bazy, nie musisz dodawać ramienia dla każdego podtypu przejściowego w celu spełnienia wyczerpującości. Podsumpcja między ramionami nadal działa tak samo jak w przypadku każdej hierarchii klas: ramię zgodne z typem podstawowym obejmuje każdy podtyp, a późniejsze ramię, które pasuje do jednego z tych podtypów, nie jest osiągalne. Rozważ zamknięcie Vehicle , którego bezpośrednie elementy potomne to Car i Truck, oraz element potomny Sedan pośredni zadeklarowany w innym zestawie:
// 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);
Przełączenie Vehicle jest wyczerpujące po jego obsłudze Car i Truck, mimo że Sedan istnieje. Ramię Car obejmuje każdą Sedan wartość:
public static string Category(Vehicle v) => v switch
{
Car => "car",
Truck => "truck",
// No warning. The 'Car' arm covers 'Sedan' through ordinary subtype matching.
};
Aby wysłać na Sedan w szczególności, umieść ramię przed ramieniem Car .
Car Ramię pozostaje osiągalne, ponieważ nadal pasuje do każdegoCar, co nie jest :Sedan
public static string CategorySedanFirst(Vehicle v) => v switch
{
Sedan => "sedan",
Car => "car", // Reachable: 'Car' values that aren't 'Sedan'.
Truck => "truck",
};
Odwrócenie tych dwóch broni powoduje błąd podsumpcji, podobnie jak w każdej innej hierarchii klas. Kompilator wykrywa, że Car ramię już obejmuje Sedanelement :
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",
};
Jeśli chcesz, aby wyczerpującość postępowała zgodnie z hierarchią w dalszej części, zadeklaruj Car się jako closed. Następnie kompilator traktuje każdy bezpośredni element potomny Car (na przykład Sedan) jako wyczerpujący zestaw zakorzeniony w .Car Przełącznik, którego typ zarządzający jest Car wyczerpujący, gdy wykonuje dowolne z następujących czynności:
- Obsługuje każdy bezpośredni obiekt potomny z własnym ramieniem
Car. -
CarZawiera ramię, które obejmuje każdąCarwartość (w tym wszystkie podtypy). - Zawiera ramię odrzucania (
_ => ...) lub ramię dla typu podstawowegoCar, takiego jakVehicle.
Przełącznik, którego typ zarządzający jest Vehicle wyborem: kod, który obsługuje Car i Truck jest nadal wyczerpujący, ponieważ Car ramię obejmuje każdy podtyp Car. Oznaczanie Carclosed po prostu daje drugą opcję dla tego przełącznika. Można zachować pojedyncze Car ramię lub zastąpić go jednym ramieniem na bezpośredni element potomny Car (obok Truck ramienia) i nadal być wyczerpujące.
Oznaczanie Carclosed powoduje również, że niejawnie abstract, co oznacza, że nie można już tworzyć wystąpień Car bezpośrednio. To może nie pasować do projektu. Jeśli musisz Car zachować możliwość utworzenia wystąpienia, pozostaw je otwarte i wyślij na określone podtypy, o które dbasz, porządkując ramiona, jak pokazano wcześniej.
Typy zarządzania parametrami typu
Wyrażenie switch , którego typ zarządzający jest parametrem typu ograniczonym do zamkniętej klasy, jest wyczerpujące na tych samych terminach co przełącznik nad samą klasą zamkniętą. Aby wysłać do zamkniętej hierarchii z kodu ogólnego, ogranicz parametr typu do zamkniętej bazy i obsłuż każdy bezpośredni element potomny:
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.
};
Aby uzyskać więcej informacji, zobacz modyfikator zamknięty. Aby uzyskać specyfikację, zobacz Zamknięte hierarchie.
Wzorce unii
Począwszy od języka C# 15, gdy wartość przychodząca wzorca jest typem unii, wzorce automatycznie odpakuj unię. Mają zastosowanie do właściwości unii Value , a nie samej wartości unii. To zachowanie sprawia, że związek jest niewidoczny dla dopasowania wzorca:
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,
};
Dwa wzorce to wyjątki: var wzorzec i wzorzec odrzucenia _ mają zastosowanie do samej wartości unii, a nie jej Value właściwości.
Wzorzec null sprawdza, czy union ma Value wartość null. W przypadku związków opartych na klasach następuje również powodzenie, null gdy odwołanie do samej unii ma wartość null.
Gdy typ unii udostępnia wzorzec dostępu bez pola (HasValuei TryGetValue elementów członkowskich), kompilator używa tych elementów członkowskich, aby uniknąć przypadków typu wartości podczas dopasowywania wzorca.
Aby uzyskać więcej informacji, zobacz Dopasowywanie unii. Aby zapoznać się ze specyfikacją, zobacz Unions (Związki).
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz sekcję Wzorce i dopasowywanie wzorcówspecyfikacji języka C#, w tym: