11 Wzorce i dopasowywanie wzorców

11.1 Ogólne

Wzorzec może być używany z is operatorem (§12.15.12), w switch_statement (§13.8.3) i w switch_expression (§12.12) w celu opisania kształtu danych, względem których mają być porównywane dane przychodzące. Wzorce mogą być zagnieżdżone, a części danych są dopasowywane do wzorców podrzędnych.

Wzorzec jest testowany względem wartości w wielu kontekstach:

  • W switch_statementwzorzecswitch_label jest testowany względem selector_expressionswitch_statement.
  • Za pomocą operatora is-pattern wzorzec po prawej stronie jest testowany względem wyrażenia po lewej stronie.
  • W switch_expressionwzorzecswitch_expression_arm jest testowany względem wyrażenia po lewej stronie switch_expression.
  • W kontekstach zagnieżdżonych wzorzec jest testowany względem wartości pobranych z właściwości, pól lub indeksowanych z innych wartości wejściowych, w zależności od formularza wzorca.

Wartość, względem której testowany jest wzorzec, jest nazywana wartością wejściową wzorca.

Formularze wzorca 11.2

11.2.1 Ogólne

Wzorzec może mieć jedną z następujących form:

pattern
    : logical_pattern
    ;

primary_pattern
    : parenthesized_pattern
    | declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    | type_pattern
    | relational_pattern
    | logical_pattern
    | list_pattern
    | slice_pattern
    ;

Produkcja '(' pattern ')' umożliwia ujęcie wzorca w nawiasy, aby wymusić kolejność oceny między wzorcami połączonymi przy użyciu jednego z logical_patterns.

Jeżeli dane wejściowe można uznać za składniowo zarówno constant_pattern , jak i positional_pattern należy wybrać constant_pattern .

Niektóre wzorcemogą spowodować deklarację zmiennej lokalnej.

Każdy formularz wzorca definiuje zestaw typów dla wartości wejściowych, do których można zastosować wzorzec. Wzorzec P ma typów, których wartości mogą być zgodne ze wzorcem. Jest to błąd czasu kompilacji, jeśli wzorzec pojawia się w programie, aby dopasować wartość wejściową wzorca P (§11.1) typu T , jeśli P nie ma zastosowania do T.

Przykład: Poniższy przykład generuje błąd czasu kompilacji, ponieważ typ czasu kompilacji v to TextReader. Zmienna typu TextReader nigdy nie może mieć wartości zgodnej z elementem string:

TextReader v = Console.In; // compile-time type of 'v' is 'TextReader'
if (v is string) // compile-time error
{
    // code assuming v is a string
}

Jednak następujące polecenie nie generuje błędu czasu kompilacji, ponieważ typ czasu kompilacji v to object. Zmienna typu object może mieć wartość zgodną z odwołaniem:string

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

przykład końcowy

Każdy formularz wzorca definiuje zestaw wartości, dla których wzorzec pasuje do wartości w czasie wykonywania.

Kolejność oceny operacji i skutków ubocznych podczas dopasowywania wzorca (wywołania do , dostęp do Deconstructwłaściwości i wywołania elementów członkowskich System.Runtime.CompilerServices.ITuple) nie są określone.

Wzorzec deklaracji 11.2.2

Declaration_pattern służy do testowania, czy wartość ma dany typ i, jeśli test zakończy się pomyślnie, opcjonalnie podaj wartość w zmiennej tego typu.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

W przypadku uznania simple_designation , jeśli mają zastosowanie zarówno discard_designation , jak i single_variable_designation alternatywy, wybiera się pierwszy.

Uwaga: ANTLR automatycznie wybiera określony wybór ze względu na kolejność alternatyw simple_designation. notatka końcowa

Typ środowiska uruchomieniowego wartości jest testowany względem typu we wzorcu przy użyciu tych samych reguł określonych w operatorze is-type (§12.15.12.1). Jeśli test zakończy się pomyślnie, wzorzec pasuje do tej wartości. Jest to błąd czasu kompilacji, jeśli typ jest typem wartości dopuszczalnej wartości null (§8.3.12) lub typem odwołania dopuszczanym do wartości null (§8.9.3). Ten formularz wzorca nigdy nie pasuje do null wartości.

Uwaga: wyrażenie e is T typu is i wzorzec e is T _ deklaracji są równoważne, gdy T nie jest typem dopuszczalnym wartości null. notatka końcowa

Biorąc pod uwagę wartość wejściową wzorca (§11.1) e, jeśli simple_designation jest discard_designation, oznaczając odrzucenie (§9.2.9.2), wartość e nie jest powiązana z niczym. W przeciwnym razie, jeśli simple_designation jest single_variable_designation, wprowadzana jest zmienna lokalna (§9.2.9) danego typu o nazwie podanego identyfikatora. Ta zmienna lokalna ma przypisaną wartość wartości wejściowej wzorca, gdy wzorzec jest zgodny z wartością.

Uwaga: To traktowanie _ w declaration_pattern różni się od autonomicznego _ napisanego jako wzorzec (§11.2.7): w drugim przypadku stała w zakresie lub typ o nazwie _, jeśli istnieje, nie jest ukryta. notatka końcowa

E Typ jest zgodny ze wzorcem zgodnym z typemT, jeśli istnieje konwersja tożsamości, niejawna lub jawna konwersja odwołania, konwersja boksu, konwersja rozpieszczania lub niejawna lub jawna konwersja typu wartości dopuszczającej wartość null z E do T, lub jeśli E albo jest T typem otwartym (§8.4.3). Wzorzec deklaracji nazewnictwa typu Tma zastosowanie do (§11.2.1) każdego typu E , dla którego E wzorzec jest zgodny z T. Jest to błąd czasu kompilacji, jeśli wzorzec deklaracji nazewnictwa typu T jest używany do dopasowania wartości wejściowej wzorca (§11.1), którego typ E statyczny nie jest zgodny ze Twzorcem .

Uwaga: Obsługa typów otwierania może być najbardziej przydatna podczas sprawdzania typów, które mogą być typami struktur lub klas, i należy unikać tworzenia pól. notatka końcowa

Przykład: Wzorzec deklaracji jest przydatny do wykonywania testów typu czasu wykonywania typów odwołań i zastępuje idiom

var v = expr as Type;
if (v != null) { /* code using v */ }

z nieco bardziej zwięzłym

if (expr is Type v) { /* code using v */ }

przykład końcowy

Przykład: Wzorzec deklaracji może służyć do testowania wartości typów dopuszczanych do wartości null: wartość typu Nullable<T> (lub pole) Tpasuje do wzorca T2 id typu, jeśli wartość jest inna niż null i T2 ma Twartość , lub jakiś podstawowy typ lub interfejs T. Na przykład w fragmentie kodu

int? x = 3;
if (x is int v) { /* code using v */ }

Warunek instrukcji if jest true w czasie wykonywania, a zmienna v przechowuje wartość 3 typu int wewnątrz bloku. Po zablokowaniu zmienna v znajduje się w zakresie, ale na pewno nie jest przypisana. przykład końcowy

Wzorzec stałej 11.2.3

Constant_pattern służy do testowania wartości wartości wejściowej wzorca (§11.1) względem danej wartości stałej.

constant_pattern
    : constant_expression
    ;

Wzorzec stałej ma P typu, jeśli istnieje niejawna konwersja z wyrażenia stałego T na typ P.T

W przypadku wzorca Pstałego jego przekonwertowana wartość to

  • jeśli typ wartości wejściowej wzorca jest typem całkowitym lub typem wyliczeniowym, stała wartość wzorca przekonwertowana na ten typ; inaczej
  • jeśli typ wartości wejściowej wzorca jest wersją typu całkowitego dopuszczaną do wartości null lub typem wyliczenia, stała wartość wzorca przekonwertowana na jego typ bazowy; inaczej
  • wartość stałej wartości wzorca.

Biorąc pod uwagę wartość wejściową wzorca e i stały wzorzec P z przekonwertowaną wartością v,

  • jeśli e ma typ całkowity lub typ wyliczeniowy, albo możliwą do wartości null formę jednego z nich, a v ma typ całkowity, wzorzec P pasuje do wartości e, jeśli wynik wyrażenia e == v to true; w przeciwnym razie
  • Jeśli wartość e jest typu System.Span<char> lub System.ReadOnlySpan<char>, a v jest ciągiem stałym, a v nie ma stałej nullwartości , wzorzec jest traktowany jako zgodny, jeśli System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(v)) zwraca truewartość ; w przeciwnym razie
  • wzorzec Ppasuje do wartości e , jeśli object.Equals(e, v) zwraca wartość true.

Przykład: instrukcja switch w poniższej metodzie używa pięciu wzorców stałych w etykietach przypadków.

static decimal GetGroupTicketPrice(int visitorCount)
{
    switch (visitorCount) 
    {
        case 1: return 12.0m;
        case 2: return 20.0m;
        case 3: return 27.0m;
        case 4: return 32.0m;
        case 0: return 0.0m;
        default: throw new ArgumentException(...);
    }
}

przykład końcowy

Wzorzec wariancja 11.2.4

Var_pattern pasuje do każdej wartości. Oznacza to, że operacja dopasowywania wzorca z var_pattern zawsze kończy się powodzeniem.

Var_pattern ma zastosowanie do każdego typu.

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

Biorąc pod uwagę wartość wejściową wzorca (§11.1) e, jeśli oznaczenie jest discard_designation, oznacza odrzucenie (§9.2.9.2), a wartość e nie jest powiązana z niczym. (Mimo że zadeklarowana zmienna o tej nazwie może znajdować się w zakresie w tym momencie, ta nazwana zmienna nie jest widoczna w tym kontekście). W przeciwnym razie, jeśli oznaczenie jest single_variable_designation, w czasie wykonywania wartość e jest powiązana z nowo wprowadzoną zmienną lokalną (§9.2.9) tej nazwy, której typem jest typ statyczny e, a wartość wejściowa wzorca jest przypisana do tej zmiennej lokalnej.

Jest to błąd, jeśli nazwa var będzie wiązać się z typem , w którym jest używana var_pattern .

Jeśli oznaczenie jest tuple_designation, wzorzec jest odpowiednikiem positional_pattern (§11.2.5) (var formularza, ... ) gdzie oznaczenias znajdują się w tuple_designation. Na przykład wzorzec jest odpowiednikiem var (x, (y, z)).(var x, (var y, var z))

11.2.5 Wzorzec pozycyjny

Positional_pattern sprawdza, czy wartość wejściowa nie nulljest , wyodrębnia z niej sekwencję wartości i pasuje do każdej wyodrębnionej wartości względem odpowiedniego podwzorca. Wartości są wyodrębniane na jeden z trzech sposobów: traktując dane wejściowe jako krotkę, wywołując metodę Deconstruct lub indeksując dane wejściowe za pomocą System.Runtime.CompilerServices.ITuplemetody .

Uwaga: użycie tego Deconstruct elementu różni się od przekształcenia dekonstrukcji na poziomie źródłowym zdefiniowanego w §12.7. notatka końcowa

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | subpattern_name ':' pattern
    ;
subpattern_name
    : identifier
    | subpattern_name '.' identifier
    ;

Niech n będzie liczbą podwzorcówwyświetlanych między nawiasami. Strategia dopasowywania jest wybierana w czasie kompilacji, stosując następujące przypadki w kolejności; pierwszy przypadek, którego warunki są spełnione, jest używany, a pozostałe przypadki nie są brane pod uwagę. Po wybraniu przypadku ta strategia zostanie zatwierdzona: zgłaszany jest dowolny błąd czasu kompilacji w tym przypadku, a dopasowanie nie przechodzi do kolejnego przypadku.

  1. Formularz krotki. Jeśli typ zostanie pominięty, a typ statyczny wartości wejściowej jest typem krotki (§8.3.11) lub jeśli wartość wejściowa jest literałem krotki (§12.8.6), dotyczy to przypadku. Jest to błąd czasu kompilacji, jeśli n nie jest równe arity tego typu krotki. W czasie wykonywania każdy element krotki jest dopasowany do odpowiedniego podwzorca; dopasowanie powiedzie się, jeśli wszystkie te rezultaty powiedzą się. Jeśli jakikolwiek podwzorca ma identyfikator, ten identyfikator będzie określać element krotki w odpowiedniej pozycji w typie krotki.
  2. Dekonstrukcja formularza. W przeciwnym razie, jeśli typ jest obecny lub typ zostanie pominięty, a statyczny typ wartości wejściowej zawiera dostępną Deconstruct metodę (§12.7), dotyczy tego przypadku. Wpisz D, jeślityp jest obecny; w przeciwnym razie niech D będzie statycznym typem wartości wejściowej. Deconstruct Metoda jest wybierana z D przy użyciu tych samych reguł rozwiązywania przeciążeń co dla deklaracji dekonstrukcji, z dodatkowym wymaganiem, że jego liczba parametrów out jest równa n; jest to błąd czasu kompilacji, jeśli taka metoda nie istnieje. Jeśli typ jest obecny, jest to błąd czasu kompilacji, jeśli statyczny typ wartości wejściowej nie jest zgodny ze wzorcem (§11.2.2) z typem; w czasie wykonywania wartość wejściowa jest testowana względem typu i, jeśli test zakończy się niepowodzeniem, dopasowanie wzorca pozycyjnego zakończy się niepowodzeniem. W przeciwnym razie wartość wejściowa jest konwertowana na D , a wybrana Deconstruct metoda jest wywoływana przy użyciu nowych zmiennych odbierających jego out parametry. Każda odebrana wartość jest dopasowywana do odpowiedniego podwzorca, a dopasowanie powiedzie się, jeśli wszystkie te elementy powiedzą się. Jeśli jakikolwiek podwzorca ma identyfikator, ten identyfikator będzie określać parametr na odpowiedniej pozycji Deconstruct.
  3. Formularz ITuple. W przeciwnym razie, jeśli typ zostanie pominięty, żaden podwzorca nie ma identyfikatora, a statyczny typ wartości wejściowej to object, System.Runtime.CompilerServices.ITuplelub typ, który ma niejawną konwersję odwołania do System.Runtime.CompilerServices.ITuple, wówczas ten przypadek ma zastosowanie. W czasie wykonywania wartość wejściowa jest testowana pod kątem wystąpienia innegonull niż ; jeśli ten test zakończy się niepowodzeniem, dopasowanie wzorca pozycyjnego System.Runtime.CompilerServices.ITuplezakończy się niepowodzeniem. W przeciwnym razie właściwość wartości Length jest odczytywana i, jeśli nie jest równa n, dopasowanie wzorca pozycyjnego kończy się niepowodzeniem. W przeciwnym razie dla każdego elementu i z zakresu od 1 do n wartość uzyskana przez indeksowanie wartości wejściowej i − 1 jest zgodna z podwzorcąi-th, a dopasowanie powiedzie się, jeśli wszystkie te operacje powiedzą się.
  4. W przeciwnym razie nie ma zastosowania przypadek, a positional_pattern jest błędem czasu kompilacji.

Kolejność dopasowania podwzorców w czasie wykonywania jest nieokreślona, a dopasowanie nie powiodło się, może nie być zgodne ze wszystkimi podwzorcami.

Przykład: w tym miejscu dekonstrukujemy wynik wyrażenia i dopasujemy wartości wynikowe do odpowiednich zagnieżdżonych wzorców:

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

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

przykład końcowy

Przykład: Nazwy elementów krotki i parametrów Deconstruct mogą być używane w wzorcu pozycyjnym w następujący sposób:

var numbers = new List<int> { 10, 20, 30 };
if (SumAndCount(numbers) is (Sum: var sum, Count: var count))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");
}

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

Generowane dane wyjściowe są następujące:

Sum of [10 20 30] is 60

przykład końcowy

Wzorzec właściwości 11.2.6

Property_pattern sprawdza, czy wartość wejściowa nie nulljest wartością , a rekursywnie pasuje do wartości wyodrębnionych przez użycie dostępnych właściwości lub pól.

property_pattern
    : type? property_subpattern simple_designation?
    ;
property_subpattern
    : '{' '}'
    | '{' subpatterns ','? '}'
    ;

Jest to błąd, jeśli jakikolwiek podwzorcaproperty_pattern nie zawiera subpattern_name.

Jest to błąd czasu kompilacji, jeśli typ jest typem wartości dopuszczalnej wartości null (§8.3.12) lub typem odwołania dopuszczanym do wartości null (§8.9.3).

Uwaga: Wzorzec sprawdzania wartości null wypada ze wzorca właściwości trywialnych. Aby sprawdzić, czy ciąg s jest inny niż null, można napisać dowolny z następujących formularzy:

#nullable enable
string s = "abc";
if (s is object o) ...  // o is of type object
if (s is string x1) ... // x1 is of type string
if (s is {} x2) ...     // x2 is of type string
if (s is {}) ...

notatka końcowa

Biorąc pod uwagę dopasowanie wyrażenia e dopodwzorców}typu{ wzorca, jest to błąd czasu kompilacji, jeśli wyrażenie e nie jest zgodne ze wzorcem (§11.2.2) z typem T wyznaczonym przez typ. Jeśli typ jest nieobecny, przyjmuje się, że typ jest statycznym typem e. Każda subpattern_name widoczna po lewej stronie jej podwzorców wyznacza dostępną czytelną właściwość lub pole T. Jeśli simple_designationproperty_pattern jest obecny, deklaruje zmienną wzorca typu T.

W czasie wykonywania wyrażenie jest testowane względem języka T. Jeśli to się nie powiedzie, dopasowanie wzorca właściwości zakończy się niepowodzeniem, a wynikiem jest false. Jeśli to się powiedzie, każde pole lub właściwość property_subpattern jest odczytywane, a jego wartość jest dopasowywana do odpowiadającego mu wzorca. Wynik całego dopasowania jest false tylko wtedy, gdy wynik któregokolwiek z nich to false. Kolejność dopasowania podwzorców nie jest określona, a dopasowanie nie powiodło się, może nie przetestować wszystkich podwzorców w czasie wykonywania. Jeśli dopasowanie powiedzie się, a simple_designationproperty_pattern jest single_variable_designation, zadeklarowana zmienna zostanie przypisana do dopasowanej wartości.

Property_pattern może służyć do dopasowywania wzorca do typów anonimowych.

Subpattern_name może odwoływać się do zagnieżdżonego elementu członkowskiego. W takim przypadku odbiornik dla każdej nazwy odnośnika jest typem poprzedniego elementu członkowskiego T₀, począwszy od typu wejściowegoproperty_pattern. Jeśli T jest typem dopuszczalnym wartości null, T₀ jest jego typem bazowym, w przeciwnym razie T₀ jest równy T. Na przykład wzorzec formularza { Prop1.Prop2: pattern } jest dokładnie taki sam jak { Prop1: { Prop2: pattern } }.

Uwaga: obejmuje to sprawdzanie wartości null, gdy T jest typem wartości dopuszczanej do wartości null lub typem odwołania. To sprawdzanie wartości null oznacza, że dostępne właściwości zagnieżdżone będą właściwościami T₀, a nie T. Ze względu na to, że wielokrotne ścieżki składowe są dozwolone, kompilacja dopasowywania wzorców może korzystać z typowych części wzorców. notatka końcowa

Przykład:

var o = ...;
if (o is string { Length: 5 } s) ...

przykład końcowy

Przykład: Sprawdzanie typu czasu wykonywania i deklaracja zmiennej można dodać do wzorca właściwości w następujący sposób:

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

Generowane dane wyjściowe są następujące:

Hello
Hi!
12345
abc

przykład końcowy

11.2.7 Odrzuć wzorzec

Każde wyrażenie jest zgodne ze wzorcem odrzucenia, co powoduje odrzucenie wartości wyrażenia.

discard_pattern
    : '_'
    ;

Jeśli kontekst składniowy zezwala na wzorzec, jeśli token _ zostanie rozpoznany jako simple_name (§12.8.4) do dostępnej stałej lub typu, _nie jest traktowany jako discard_pattern. Zamiast:

  • Jeśli _ jest rozpoznawana jako stała dostępna, _ element jest interpretowany jako constant_pattern (§11.2.3), którego stałe wyrażenie jest stałą.
  • Jeśli _ rozpozna typ, po prawej stronie is operatora konstrukcja relational_expressionis _ jest interpretowana jako operator typu is (§12.15.12.12.1) dla tego typu. W każdym innym kontekście składniowym, który przyznaje wzorzec, nagie _ rozpoznawanie typu nie jest prawidłowym wzorcem, jednak _ może być wyświetlany jako typdeclaration_pattern (np. ) lub w innych formularzach wzorców, _ xktóre jawnie nazywają typ.

Ta reguła zachowuje zgodność wsteczną z kodem zdefiniowanym _ jako typ lub identyfikator przed wprowadzeniem wzorca odrzucenia. Jeśli _ rozpoznaje coś innego niż dostępna stała lub typ (na przykład zmienna lokalna, parametr, pole lub metoda), reguła nie ma zastosowania i _ pozostaje discard_pattern.

Uwaga: Jest to analogia do reguły w var§11.2.4, z tą różnicą, że dla _ stałej lub typu w zakresie jest _ interpretowana jako odwołanie do tej deklaracji, a nie generowanie błędu. notatka końcowa

Jeśli po zastosowaniu poprzedniej reguły token _ jest nadal discard_pattern, jest to błąd czasu kompilacji, który discard_pattern wyświetlić jako cały wzorzecrelational_expression formularza relational_expressioniswzorzec lub jako cały wzorzecswitch_label. Discard_pattern może jednak występować jako podwzorca otaczającego wzorca (na przykład jako podwzorcapositional_pattern lub property_pattern).

Uwaga: W takich przypadkach, aby dopasować dowolne wyrażenie, użyj var_pattern z odrzuceniem var _. notatka końcowa

Przykład:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));
Console.WriteLine(GetDiscountInPercent(null));
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));

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

Generowane dane wyjściowe są następujące:

5.0
0.0
0.0

Tutaj wzorzec odrzucenia jest używany do obsługi null i wszystkich wartości całkowitych, które nie mają odpowiedniego DayOfWeek elementu członkowskiego wyliczenia. Gwarantuje to, że switch wyrażenie obsługuje wszystkie możliwe wartości wejściowe. przykład końcowy

Przykład: W poniższym przykładzie pokazano, jak stała w zakresie zmienia _ interpretację _ ramienia w wyrażeniu switch . W WithoutUnderscoresystemie _ ramię jest discard_pattern i pasuje do dowolnej wartości. W WithUnderscoreobiekcie stała _ w zakresie powoduje _ , że ramię jest interpretowane jako constant_pattern , która odpowiada tylko wartości 0.

static string WithoutUnderscore(int n) => n switch
{
    1 => "one",
    _ => "other",
};

static string WithUnderscore(int n)
{
    const int _ = 0;
    return n switch
    {
        1 => "one",
        _ => "zero",
        var x => "other: " + x,
    };
}

przykład końcowy

Wzorzec typu 11.2.8

Type_pattern służy do testowania, czy wartość wejściowa wzorca (§11.1) ma określony typ.

type_pattern
    : type
    ;

Wzorzec typu nazewnictwa typu Tma zastosowanie do każdego typu E , dla którego Ewzorzec jest zgodny z T (§11.2.2).

Typ środowiska uruchomieniowego wartości jest testowany względem typu przy użyciu tych samych reguł określonych w operatorze is-type (§12.15.12.1). Jeśli test zakończy się pomyślnie, wzorzec pasuje do tej wartości. Jest to błąd czasu kompilacji, jeśli typ jest typem dopuszczalnym wartości null. Ten formularz wzorca nigdy nie pasuje do null wartości.

Wzorzec relacyjny 11.2.9

Relational_pattern służy do relacyjnej testowania wartości wejściowej wzorca (§11.1) względem wartości stałej.

relational_pattern
    : '<'  relational_expression
    | '<=' relational_expression
    | '>'  relational_expression
    | '>=' relational_expression
    ;

Do obliczenia wartości stałej wymagane jest relational_expression w relational_pattern .

Wzorce relacyjne obsługują operatory <relacyjne , <=, >i >= we wszystkich wbudowanych typach, które obsługują takie operatory relacyjne binarne z obydwoma operandami o tym samym typie: sbyte, byteshortushortintuintlongulongcharfloatdoubledecimalnintnuinti wyliczenia.

Jest to błąd czasu kompilacji, jeśli constant_expressionto double.NaN, float.NaNlub null_literal.

Gdy wartość wejściowa ma typ, dla którego jest zdefiniowany odpowiedni wbudowany operator relacyjny binarny, ocena tego operatora jest traktowana jako znaczenie wzorca relacyjnego. W przeciwnym razie wartość wejściowa jest konwertowana na typ constant_expression przy użyciu jawnej konwersji dopuszczającej wartość null lub odboxingu. Jest to błąd czasu kompilacji, jeśli taka konwersja nie istnieje. Jest to błąd czasu kompilacji, jeśli typ danych wejściowych jest ograniczony do lub typ dziedziczony, a System.Numerics.INumberBase<T> typ wejściowy nie ma odpowiedniego wbudowanego operatora relacyjnego binarnego. Wzorzec jest uznawany za niezgodny, jeśli konwersja nie powiedzie się. Jeśli konwersja powiedzie się, wynikiem operacji dopasowywania wzorca jest ocena wyrażenia e «op» v , w którym e jest przekonwertowany dane wejściowe, «op» jest operatorem relacyjnym i v jest constant_expression.

Przykład:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(double.NaN));
Console.WriteLine(Classify(2.4));

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

Generowane dane wyjściowe są następujące:

Too high
Unknown
Acceptable

przykład końcowy

11.2.10 Wzorzec logiczny

Logical_pattern służy do negowania wyniku dopasowania wzorca lub łączenia wyników wielu dopasowań wzorca przy użyciu kombinacji (and) lub rozsyłania (or).

logical_pattern
    : disjunctive_pattern
    ;

disjunctive_pattern
    : disjunctive_pattern 'or' conjunctive_pattern
    | conjunctive_pattern
    ;

conjunctive_pattern
    : conjunctive_pattern 'and' negated_pattern
    | negated_pattern
    ;

negated_pattern
    : 'not' negated_pattern
    | primary_pattern
    ;

not, andi or są zbiorczo nazywane operatorami wzorców.

Negated_pattern pasuje, jeśli negowany wzorzec nie jest zgodny i odwrotnie. Conjunctive_pattern wymaga dopasowania obu wzorców. Disjunctive_pattern wymaga dopasowania obu wzorców. W przeciwieństwie do ich odpowiedników operatorów językowych && i ||, and i ornie są operatorami zwarciowymi.

Jest to błąd czasu kompilacji dla zmiennej wzorca, która ma być zadeklarowana pod operatorem not lub or wzorca.

Uwaga: ponieważ ani notor nie może utworzyć określonego przypisania dla zmiennej wzorca, jest to błąd deklarowania jednego w tych pozycjach. notatka końcowa

W conjunctive_patterntyp wejściowy drugiego wzorca jest zawężany przez typ zawężający wymagania pierwszego wzorca .and Zawężony typ wzorca P jest definiowany w następujący sposób:

  • Jeśli P jest wzorcem typu, zawężony typ jest typem typu wzorca typu.
  • W przeciwnym razie, jeśli P jest wzorcem deklaracji, zawężony typ jest typem typu wzorca deklaracji.
  • W przeciwnym razie, jeśli P jest wzorcem rekursywnym, który daje jawny typ, zawężonego typu jest ten typ.
  • W przeciwnym razie, jeśli P jest dopasowywany za pośrednictwem reguł w ITuplepositional_pattern (§11.2.5), zawężony typ to typ System.ITuple.
  • W przeciwnym razie, jeśli jest stałym wzorcem, w P którym stała nie jest stałą null i gdzie wyrażenie nie ma stałej konwersji wyrażenia na typ danych wejściowych, zawężony typ jest typem stałej.
  • W przeciwnym razie, jeśli P jest wzorcem relacyjnym, w którym wyrażenie stałe nie ma stałej konwersji wyrażenia na typ wejściowy, zawężony typ jest typem stałej.
  • W przeciwnym razie, jeśli P jest wzorcem or , zawężony typ jest typowym typem zawężonego typu podwzorców, jeśli taki typ wspólny istnieje. W tym celu algorytm wspólnego typu uwzględnia tylko konwersje tożsamości, boksu i niejawnych odwołań oraz uwzględnia wszystkie podwzory sekwencji or wzorców (ignorując wzorce nawiasów).
  • W przeciwnym razie, jeśli P jest wzorcem and , zawężony typ jest zawężonym typem odpowiedniego wzorca. Ponadto zawęziony typ wzorca po lewej stronie jest typem wejściowym odpowiedniego wzorca.
  • W przeciwnym razie zawęziony typP jest Ptypem wejściowym.

Uwaga: zgodnie z gramatyką not ma pierwszeństwo przed andwartością , która ma pierwszeństwo przed or. Można to jawnie wskazać lub zastąpić za pomocą nawiasów. notatka końcowa

Gdy wzorzec pojawia się po prawej stronie is, zakres wzorca jest określany przez gramatykę; w rezultacie operatory andwzorca , ori not w obrębie wzorca wiążą się ściślej niż operatory &&logiczne , ||i ! poza wzorcem.

Przykład:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(-100));
Console.WriteLine(Classify(5.7));

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

Generowane dane wyjściowe są następujące:

High
Too low
Acceptable

przykład końcowy

Przykład:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));

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

Generowane dane wyjściowe są następujące:

winter
autumn
spring

przykład końcowy

Przykład:

object msg = "msg";
object obj = 5;
bool flag = true;

// This is parsed as: (msg is (not int) or string)
result = msg is not int or string;
Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}");

// This is parsed as: (obj is (int or string)) && flag
bool result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}");

flag = false;
// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}");

Generowane dane wyjściowe są następujące:

msg ("msg"): msg is not int or string: True
obj (5), flag (true): obj is int or string && flag: True
obj (5), flag (true): obj is int || obj is string && flag: True
obj (5), flag (false): obj is int or string && flag: False
obj (5), flag (false): obj is int || obj is string && flag: True

przykład końcowy

Wzorzec listy 11.2.11

List_pattern pasuje do sekwencji elementów na liście lub tablicy.

list_pattern
    : list_pattern_clause simple_designation?
    ;

list_pattern_clause
    : '[' (pattern (',' pattern)* ','?)? ']'
    ;

List_pattern jest zgodny z dowolnym typem, który jest zliczalny (§18.1), a także indeksowalny (§18.1) — ma dostępny indeksator, który przyjmuje Index jako argument lub dostępny indeksator z pojedynczym int parametrem. Jeśli oba indeksatory są obecne, preferowane jest poprzednie. (Zobacz §18.4.2 , aby uzyskać szczegółowe informacje o niejawnej obsłudze indeksów).

Wzorzec formularza expr is [1, 2, 3] jest odpowiednikiem następującego kodu:

expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3

Przykład:

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 and not 7]);  // True

przykład końcowy

Wzorzec odrzucenia (§11.2.7) pasuje do każdego pojedynczego elementu.

Przykład:

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

if (numbers is [_, var second, _])
{
    Console.WriteLine($"The second element is {second}.");
}

przykład końcowy

Wzorzec wycinka 11.2.12

Slice_pattern odrzuca zero lub więcej elementów. Stosuje się go tylko bezpośrednio w list_pattern_clause, a następnie tylko raz w tej klauzuli.

slice_pattern
    : '..' pattern?
    ;

Slice_pattern bez podwzorcy jest zgodny z dowolnym typem zgodnym z list_pattern. Slice_pattern z podwzorcą jest zgodny z dowolnym typem, który jest zliczalny (§18.1), a także fragmentowalny (§18.1) — ma dostępny indeksator, który przyjmuje Range jako argument lub dostępną Slice metodę z dwoma int parametrami. Jeśli oba są obecne, preferowane jest poprzednie. (Zobacz §18.4.2 , aby uzyskać szczegółowe informacje o niejawnej obsłudze indeksów).

Slice_pattern działa jak właściwe odrzucenie; oznacza to, że dla takiego wzorca nie są wykonywane żadne testy. Dotyczy to tylko innych węzłów, a mianowicie długości i indeksatora. Na przykład wzorzec formularza expr is [1, .. var s, 3] jest odpowiednikiem następującego kodu (jeśli jest zgodny za pośrednictwem jawnego Index i Range obsługiwanego):

expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3

Typ danych wejściowych dla slice_pattern jest typem zwrotnym bazowego this[Range] lub Slice metody z dwoma wyjątkami: odpowiednio dla strings i tablic string.Substring i RuntimeHelpers.GetSubArray, należy użyć.

Przykład: Wzorzec wycinka może służyć do dopasowywania elementów tylko na początku lub/i na końcu sekwencji danych wejściowych.

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

przykład końcowy

Przykład: podwzorca można zagnieżdżać w ramach wzorca wycinka:

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

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

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

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

przykład końcowy

11.3 Podsumpcja wzorca

W instrukcji switch (§13.8.3) jest to błąd, jeśli wzorzec przypadku jest podsumowywany przez poprzedni zestaw niestrzeżonych przypadków (§13.8.3). W wyrażeniu przełącznika (§12.12) jest to błąd, jeśli wzorzec switch_expression_arm jest podsumowywany przez poprzedni zestaw niestrzeżonych wzorców switch_expression_arm.

Uwaga: Oznacza to, że każda wartość wejściowa byłaby zgodna z jedną z poprzednich spraw lub broni. notatka końcowa Następujące reguły definiują, kiedy zestaw wzorców subsumuje określony wzorzec:

Wzorzec Ppasuje do stałej K , jeśli którykolwiek z następujących warunków jest wstrzymany:

  • specyfikacją zachowania środowiska uruchomieniowego tego wzorca jest to, że P jest zgodna z Kparametrem .
  • Pjest type_pattern typu T i nie K jest, null a typ K środowiska uruchomieniowego jest T lub typ pochodzący z T lub typu, który implementuje T.
  • Pjest relational_pattern z operatorem «op» i stałą v, a wyrażenie K «op» v zwróci wartość .true
  • P jest negated_patternnot P₁ i P₁ nie pasuje Kdo .
  • P jest conjunctive_patternP₁ and P₂ , a oba P₁ będą zgodne K i P₂ pasują do K.
  • P jest disjunctive_patternP₁ or P₂ i P₁ albo K pasuje, albo P₂ pasuje Kdo .
  • P jest discard_pattern.

Zestaw wzorców Qsubsumuje wzorzec P , jeśli którykolwiek z następujących warunków jest wstrzymany:

  • Pjest stałym wzorcem, a każdy z wzorców w zestawie Q będzie zgodny z Pprzekonwertowaną wartością
  • P jest wzorcem var, a zestaw wzorców Q jest wyczerpujący (§11.4) dla typu wartości wejściowej wzorca (§11.1), a wartość wejściowa wzorca nie jest typu dopuszczalnego wartości null lub jakiś wzorzec w Q elemecie jest zgodny nullz .
  • P jest wzorcem deklaracji z typem T , a zestaw wzorców Q jest wyczerpujący dla typu T (§11.4).
  • P jest type_pattern typu T , a zestaw wzorców Q jest wyczerpujący dla typu T.
  • P jest relational_pattern z operatorem «op» i wartością stałą , a dla każdej wartości vtypu wejściowego spełniającego relację «op» v, jakiś wzorzec w zestawie Q będzie pasował do tej wartości.
  • Pto disjunctive_patternP₁ or P₂ oraz zestaw podsumowania Q i P₁ podsumowania QwzorcówP₂.
  • P jest conjunctive_patternP₁ and P₂ i co najmniej jedna z następujących blokad: Q podsumy P₁, lub Q podsumy P₂.
  • P jest negated_patternnot P₁ i Q jest wyczerpujący dla typu wejściowego, biorąc pod uwagę tylko wartości, które nie są zgodne przez P₁.
  • P jest discard_pattern , a zestaw wzorców Q jest wyczerpujący dla typu wartości wejściowej wzorca, a wartość wejściowa wzorca nie jest typu dopuszczalnego wartości null lub jakiś wzorzec w Q elemecie jest zgodny nullz .
  • Jakiś wzorzec w Q pliku to disjunctive_patternQ₁ or Q₂ i zastąpienie tego wzorca elementem Q₁ in Q spowoduje uzyskanie zestawu, który subsumuje P, lub zastąpienie go wartością Q₂ , spowoduje uzyskanie zestawu, który subsumuje P.
  • Wzorzec w pliku Q to negated_patternnot Q₁ i P nie pasuje do żadnej zgodnej wartości Q₁ .

Przykład: W poniższym wyrażeniu przełącznika żaden ramię nie jest podsumowywane, mimo że ramiona 1, 2 i 3 mają ten sam wzorzec:

object x = 10;
bool b = false;
int y = x switch
{
    int i when !b => 0,
    int i when b  => 1,
    int i         => 2,
    _             => 3
};

Ramiona 1 i 2 mają niestałych strażników i tak nie są niestrzeżone; tylko ramię 3 jest niestrzeżone ze wzorcem int i, który nie subsumuje końcowego _ ramienia, ponieważ nie pasuje do wartości innejint niż null. przykład końcowy

Sprawdzanie podsumpcji list_patterns i slice_patterndziała podobnie jak wzorce pozycyjne z ITuple (§11.2.5) odpowiednie podwzory są dopasowywane przez położenie i dodatkowy węzeł do długości testowania.

Na przykład następujący kod generuje błąd, ponieważ oba wzorce dają ten sam daG:

case [_, .., 1]: // expr.Length is >= 2 && expr[^1] is 1
case [.., _, 1]: // expr.Length is >= 2 && expr[^1] is 1

Niepodobny:

case [_, 1, ..]: // expr.Length is >= 2 && expr[1] is 1
case [.., 1, _]: // expr.Length is >= 2 && expr[^2] is 1

Biorąc pod uwagę określoną długość, możliwe, że dwa podwzorcy odnoszą się do tego samego elementu, w takim przypadku test dla tej wartości jest wstawiony do decyzji DAG.

  • Na przykład staje się [_, >0, ..] or [.., <=0, _] miejscem, length >= 2 && ([1] > 0 || length == 3 || [^2] <= 0) w którym wartość długości 3 oznacza inny test.
  • Z drugiej strony staje się [_, >0, ..] and [.., <=0, _] miejscem, length >= 2 && [1] > 0 && length != 3 && [^2] <= 0 w którym wartość długości 3 nie zezwala na inny test.

W związku z tym błąd może spowodować coś case [.., p]: case [p]: podobnego, ponieważ w czasie wykonywania ten sam element jest dopasowywany w drugim przypadku.

Jeśli podwzorca wycinka pasuje do listy lub wartości długości, podwzorce są traktowane tak, jakby były bezpośrednim podwzorcą zawierającej listę. Na przykład [..[1, 2, 3]] podsumowyuje wzorzec formularza [1, 2, 3].

Na używanych elementach członkowskich przyjmuje się następujące założenia:

  • Przyjmuje się, że właściwość, która sprawia, że typ jest zliczalny , zawsze zwraca wartość nieujemną, jeśli i tylko wtedy, gdy typ jest indeksowalny. Na przykład wzorzec { Length: -1 } nigdy nie może być zgodny z tablicą.
  • Element członkowski, który sprawia, że typ można podzielić na fragmentatory , przyjmuje się, że jest dobrze zachowywany; oznacza to, że wartość zwracana nigdy nie null jest i że jest to odpowiedni podsieć listy zawierającej.

Zachowanie operacji dopasowywania wzorca jest niezdefiniowane, jeśli którekolwiek z powyższych założeń nie jest przechowywane. [

11.4 Wyczerpującość wzorca

Nieformalnie zestaw wzorców jest wyczerpujący dla typu, jeśli dla każdej możliwej wartości tego typu innej niż null, jakiś wzorzec w zestawie ma zastosowanie. Następujące reguły określają, kiedy zestaw wzorców jest wyczerpujący dla typu:

Zestaw wzorców Q jest wyczerpujący dla typu T , jeśli którykolwiek z następujących warunków jest wstrzymany:

  1. T jest typem całkowitym lub wyliczeniowym albo wersją dopuszczającą wartość null jednej z tych wartości i dla każdej możliwej wartości typu bazowego Tbez wartości null, jakiś wzorzec w Q elemecie pasuje do tej wartości; lub
  2. Wzorzec w pliku Q jest wzorcem var; lub
  3. Wzorzec w pliku Q jest wzorcem deklaracji dla typu D, a istnieje konwersja tożsamości, niejawna konwersja odwołania lub konwersja boksu z T na D; lub
  4. Wzorzec w pliku Q to type_pattern typu D, a istnieje konwersja tożsamości, niejawna konwersja odwołania lub konwersja boksu z T na D; lub
  5. Wzorzec w pliku Q to discard_pattern; lub
  6. Wzorce w programie Q obejmują kombinację relational_patterns i constant_patterns, których zakresy łącznie obejmują każdą możliwą wartość typu bazowego Tbez wartości null. W przypadku float typów i double obejmuje System.Double.NaN to lub System.Single.NaN odpowiednio, ponieważ NaN nie jest zgodne z żadnym wzorcem relacyjnym; lub
  7. Wzorzec w obiekcie Q to disjunctive_patternP₁ or P₂, a zastąpienie tego wzorca parametrem zarówno P₁ , jak i P₂ w Q parametrze daje zestaw wyczerpujący dla Telementu ; lub
  8. Wzorzec w pliku Q to negated_patternnot P₁, a wzorce w Q połączeniu z wartościami, które nie są zgodne z P₁ każdą możliwą wartością T. Negated_patternnot P₁ jest wyczerpująca, gdy P₁ nie pasuje do możliwej Twartości ; lub
  9. Wzorzec w pliku Q to conjunctive_patternP₁ and P₂, a zestaw zawierający tylko P₁ jest wyczerpujący, a zestaw zawierający tylko T element jest P₂ dla elementu T.

Uwaga: jeśli wzorzec typu zawiera typy dopuszczające wartość null, wzorzec może być wyczerpujący dla typu, ale nadal generuje ostrzeżenie, ponieważ wzorzec typu nie będzie zgodny z wartością null . notatka końcowa

Uwaga: w przypadku typów zmiennoprzecinkowych kombinacja wzorców < 0 i >= 0nie jest wyczerpująca, ponieważ żaden wzorzec relacyjny nie pasuje do NaNklasy . Prawidłowym zestawem wyczerpującym jest < 0, >= 0i double.NaN (lub float.NaN). notatka końcowa

Przykład:

static void M(byte b)
{
    switch (b) {
        case 0: case 1: case 2: ... // handle every specific value of byte
            break;
        // error: the pattern 'byte other' is subsumed by the (exhaustive)
        // previous cases
        case byte other: 
            break;
    }
}

przykład końcowy