11 Mönster och mönstermatchning

11.1 Allmänt

Ett mönster är en syntaktisk form som kan användas med operatorn is (§12.15.12), i en switch_statement (§13.8.3) och i en switch_expression (§12.12) för att uttrycka formen på data som inkommande data ska jämföras med. Mönster kan vara rekursiva, så att delar av data kan matchas mot undermönster.

Ett mönster testas mot ett värde i ett antal kontexter:

  • I en switch-instruktion testas mönstret för en ärendeetikett mot uttrycket för switch-instruktionen.
  • I en is-pattern-operator testas mönstret till höger mot uttrycket till vänster.
  • I ett växeluttryck testas mönstret för en switch_expression_arm mot uttrycket på växeluttryckets vänstra sida.
  • I kapslade kontexter testas undermönstret mot värden som hämtats från egenskaper, fält eller indexeras från andra indatavärden, beroende på mönsterformuläret.

Värdet som ett mönster testas mot kallas mönsterindatavärdet.

11.2 Mönsterformulär

11.2.1 Allmänt

Ett mönster kan ha något av följande formulär:

pattern
    : logical_pattern
    ;

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

parenthesized_pattern
    : '(' pattern ')'
    ;

Produktionen '(' pattern ')' gör att ett mönster kan omges av parenteser för att framtvinga utvärderingsordningen mellan mönster som kombineras med någon av de logical_patterns.

Vissa mönsterkan resultera i deklarationen av en lokal variabel.

Varje mönsterformulär definierar uppsättningen med typer för indatavärden som mönstret kan tillämpas på. Ett mönster P gäller för en typ T om T är bland de typer vars värden mönstret kan matcha. Det är ett kompileringsfel om ett mönster P visas i ett program för att matcha ett mönsterindatavärde (§11.1) av typen T om P det inte är tillämpligt på T.

Exempel: I följande exempel genereras ett kompileringsfel eftersom kompileringstidstypen v är TextReader. En variabel av typen TextReader kan aldrig ha ett värde som är referenskompatibelt med 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
}

Följande genererar dock inte ett kompileringsfel eftersom kompileringstidstypen v är object. En variabel av typen object kan ha ett värde som är referenskompatibelt med string:

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

slutexempel

Varje mönsterformulär definierar den uppsättning värden som mönstret matchar värdet för vid körning.

Ordningen för utvärdering av åtgärder och biverkningar under mönstermatchning (anrop till Deconstruct, egenskapsåtkomster och anrop av metoder i System.ITuple) har inte angetts.

11.2.2 Deklarationsmönster

En declaration_pattern används för att testa att ett värde har en viss typ och, om testet lyckas, om du vill ange värdet i en variabel av den typen.

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

En simple_designation med token _ ska betraktas som en discard_designation snarare än en single_variable_designation.

Körningstypen för värdet testas mot typen i mönstret med samma regler som anges i operatorn is-type (§12.15.12.1). Om testet lyckas matchar mönstret det värdet. Det är ett kompileringsfel om typen är en nullbar värdetyp (§8.3.12) eller en nullbar referenstyp (§8.9.3). Det här mönsterformuläret matchar aldrig ett null värde.

Obs! Uttrycket e is T is-type och deklarationsmönstret e is T _ är likvärdiga när T det inte är en nullbar typ. slutkommentar

Med tanke på ett mönsterindatavärde (§11.1) e, om simple_designation är discard_designation, anger det ett ignorerande (§9.2.9.2), och värdet på e är inte bundet till någonting. (Även om en deklarerad variabel med namnet _ kan finnas i omfånget vid den tidpunkten visas inte den namngivna variabeln i den här kontexten.) Om simple_designation annars är single_variable_designation introduceras en lokal variabel (§9.2.9) av den angivna typen som namnges av den angivna identifieraren. Den lokala variabeln tilldelas värdet för mönsterindatavärdet när mönstret matchar värdet.

Vissa kombinationer av statisk typ av mönsterindatavärdet och den angivna typen anses vara inkompatibla och resulterar i ett kompileringsfel. Ett värde av statisk typ E sägs vara mönsterkompatibelt med typen T om det finns en identitetskonvertering, en implicit eller explicit referenskonvertering, en boxningskonvertering, en konvertering utan inkorg eller en implicit eller explicit nullbar värdetypskonvertering från E till T, eller om antingen E eller T är en öppen typ (§8.4.3). Ett deklarationsmönster som namnger en typ gäller för varje typ T som är mönsterkompatibel med E.ET

Obs! Stödet för öppna typer kan vara mest användbart när du kontrollerar typer som kan vara antingen struct- eller klasstyper, och boxning bör undvikas. slutkommentar

Exempel: Deklarationsmönstret är användbart för att utföra körningstyptester av referenstyper och ersätter formspråket

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

med den något mer koncisa

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

slutexempel

Exempel: Deklarationsmönstret kan användas för att testa värden för null-typer: ett värde av typen Nullable<T> (eller en rutad T) matchar ett typmönster T2 id om värdet inte är null och T2 är T, eller någon bastyp eller gränssnitt för T. Till exempel i kodfragmentet

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

Villkoret för -instruktionen if är true vid körning och variabeln v innehåller värdet 3 av typen int i blocket. Efter blocket är variabeln v i omfånget, men inte definitivt tilldelad. slutexempel

11.2.3 Konstant mönster

En constant_pattern används för att testa värdet för ett mönsterindatavärde (§11.1) mot det angivna konstantvärdet.

constant_pattern
    : constant_expression
    ;

Ett konstant mönster P gäller för en typ T om det finns en implicit konvertering från det konstanta uttrycket för P till typen T.

För ett konstant mönster Pär dess konverterade värde

  • Om mönstrets indatavärdestyp är en integraltyp eller en uppräkningstyp konverteras mönstrets konstantvärde till den typen. annars
  • Om mönstrets indatavärdestyp är den nullbara versionen av en integraltyp eller en uppräkningstyp konverteras mönstrets konstantvärde till dess underliggande typ. annars
  • värdet för mönstrets konstanta värde.

Givet ett mönsterindatavärde e och ett konstant mönster P med konverterat värde v,

  • om e har en integraltyp eller uppräkningstyp, eller en nullbar form av en av dessa, och v har en integrerad typ, matchar mönstret Pvärdet e om resultatet av uttrycket e == v är true, annars
  • mönstret Pmatchar värdet e om object.Equals(e, v) returnerar true.

Exempel: Instruktionen switch i följande metod använder fem konstanta mönster i sina skiftlägesetiketter.

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

slutexempel

11.2.4 Var-mönster

Ett var_patternmatchar varje värde. En mönstermatchningsåtgärd med en var_pattern lyckas alltså alltid.

En var_pattern gäller för varje typ.

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

Givet ett mönstrat indatavärde (§11.1) e, om beteckningenär discard_designation, det antecknar en kassera (§9.2.9.2), och värdera av e är inte bundet till något. (Även om en deklarerad variabel med det namnet kan finnas i omfånget vid den tidpunkten visas inte den namngivna variabeln i den här kontexten.) Annars, om beteckningenär single_variable_designation, vid körning är värdet e bundet till en nyligen introducerad lokal variabel (§9.2.9) av det namnet vars typ är den statiska typen av e, och mönstrets indatavärde tilldelas till den lokala variabeln.

Det är ett fel om namnet var skulle binda till en typ där en var_pattern används.

Om beteckning är en tuple_designation, är mönstret likvärdigt med en positional_pattern (§11.2.5) av (var, ... ) där beteckningenär de som finns inom tuple_designation. Mönstret var (x, (y, z)) motsvarar till (var x, (var y, var z))exempel .

11.2.5 Positionsmönster

En positional_pattern kontrollerar att indatavärdet inte nullär , anropar en lämplig Deconstruct metod (§12.7) och utför ytterligare mönstermatchning på de resulterande värdena. Den stöder också en tupplarliknande mönstersyntax (utan att typen anges) när typen av indatavärde är samma som typen som innehåller Deconstruct, eller om typen av indatavärde är en tupplartyp, eller om typen av indatavärde är object eller System.ITuple och körningstypen för uttrycket implementerar System.ITuple.

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

Med tanke på en matchning av ett indatavärde med mönstertypens(undermönster), väljs en metod genom att söka i typ efter tillgängliga deklarationer av Deconstruct och välja en av dem med samma regler som för dekonstruktionsdeklarationen. Det är ett fel om en positional_pattern utelämnar typen, har ett enda undermönster utan identifierare, inte har någon property_subpattern och inte har någon simple_designation. Detta skiljer sig mellan en constant_pattern som är parentesiserad och en positional_pattern. För att extrahera de värden som ska matchas mot mönstren i listan,

  • Om typen utelämnas och indatauttryckets typ är en tuppelns typ, ska antalet undermönster vara detsamma som tuppelns kardinalitet. Varje tuppelns element matchas mot motsvarande underavsnitt och matchningen lyckas om alla dessa lyckas. Om någon underordnad har en identifierare, ska det namnge ett tupppelelement vid motsvarande position i tuppelns typ.
  • Om en lämplig Deconstruct finns som medlem av typen är det annars ett kompileringsfel om typen av indatavärde inte är mönsterkompatibel med typen. Vid körning testas indatavärdet mot typ. Om detta misslyckas misslyckas positionsmönstermatchningen. Om det lyckas konverteras indatavärdet till den här typen och Deconstruct anropas med nya kompilatorgenererade variabler för att ta emot utdataparametrarna. Varje värde som togs emot matchas mot motsvarande undermönster och matchningen lyckas om alla dessa lyckas. Om någon underordnad har en identifierare ska den ge en parameter namnet på motsvarande position Deconstruct.
  • Annars, om typen utelämnas och indatavärdet är av typen object eller någon typ som kan konverteras till System.ITuple av en implicit referenskonvertering, och ingen identifierare visas bland underavsnitten, använder System.ITuplematchningen .
  • Annars är mönstret ett kompileringsfel.

Ordningen i vilken underavsnitt matchas vid körning är ospecificerad och en misslyckad matchning kanske inte försöker matcha alla undermönster.

Exempel: Här dekonstruerar vi ett uttrycksresultat och matchar de resulterande värdena mot motsvarande kapslade mönster:

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

slutexempel

Exempel: Namnen på tuppelns element och dekonstruktionsparametrar kan användas i ett positionsmönster enligt följande:

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

De utdata som genereras är

Sum of [10 20 30] is 60

slutexempel

11.2.6 Egenskapsmönster

En property_pattern kontrollerar att indatavärdet inte nullär och matchar rekursivt värden som extraheras med hjälp av tillgängliga egenskaper eller fält.

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

Det är ett fel om ett underavsnitt av en property_pattern inte innehåller någon identifierare.

Det är ett kompileringsfel om typen är en nullbar värdetyp (§8.3.12) eller en nullbar referenstyp (§8.9.3).

Obs! Ett null-kontrollmönster faller ur ett trivialt egenskapsmönster. För att kontrollera om strängen s inte är null kan man skriva något av följande formulär:

#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 {}) ...

slutkommentar

Med tanke på en matchning av ett uttryck e tillunderordnad{mönstertyp} är det ett kompileringsfel om uttrycket e inte är mönsterkompatibelt med typen T som har angetts efter typ. Om typen saknas antas typen vara den statiska typen e. Var och en av de identifierare som förekommer till vänster om dess underordnad ska ange en tillgänglig läsbar egenskap eller ett fält i T. Om simple_designation för property_pattern finns deklarerar den en mönstervariabel av typen T.

Vid körning testas uttrycket mot T. Om detta misslyckas misslyckas egenskapsmönstermatchningen och resultatet blir false. Om det lyckas läss varje property_subpattern fält eller egenskap och dess värde matchas mot motsvarande mönster. Resultatet av hela matchningen är false bara om resultatet av något av dessa är false. Ordningen där undermönster matchas anges inte och en misslyckad matchning kanske inte testar alla undermönster vid körning. Om matchningen lyckas och simple_designation för property_pattern är en single_variable_designation tilldelas den deklarerade variabeln det matchade värdet.

Property_pattern kan användas för att mönstermatcha med anonyma typer.

Exempel:

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

slutexempel

Exempel: En körningstypkontroll och en variabeldeklaration kan läggas till i ett egenskapsmönster enligt följande:

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

De utdata som genereras är

Hello
Hi!
12345
abc

slutexempel

11.2.7 Ignorera mönster

Varje uttryck matchar mönstret ignorera, vilket resulterar i värdet för uttrycket som ignoreras.

discard_pattern
    : '_'
    ;

Det är ett kompileringsfel att använda ett ignorerande mönster i en relational_expression av formuläret relational_expressionismönster eller som mönstret för en switch_label.

Obs! Om du vill matcha alla uttryck använder du en var_pattern med ignorera var _. slutkommentar

Exempel:

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

De utdata som genereras är

5.0
0.0
0.0

Här används ett ignorerande mönster för att hantera null och alla heltalsvärden som inte har motsvarande medlem i DayOfWeek uppräkningen. Det garanterar att uttrycket switch hanterar alla möjliga indatavärden. slutexempel

11.2.8 Typmönster

En type_pattern används för att testa att indatavärdet för mönster (§11.1) har en viss typ.

type_pattern
    : type
    ;

Ett typmönster som namnger en typ Tgäller för varje typ E som E är mönsterkompatibel med T (§11.2.2).

Körningstypen för värdet testas mot typen med samma regler som anges i is-type-operatorn (§12.15.12.1). Om testet lyckas matchar mönstret det värdet. Det är ett kompileringsfel om typen är en nullbar typ. Det här mönsterformuläret matchar aldrig ett null värde.

11.2.9 Relationsmönster

En relational_pattern används för att relationstesta mönsterindatavärdet (§11.1) mot ett konstant värde.

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

Relational_expression i en relational_pattern krävs för att utvärdera till ett konstant värde.

Relationsmönster stöder relationsoperatorerna , , och på alla inbyggda typer som stöder sådana binära relationsoperatorer med samma typ: sbyte, byte, short, ushort, int, uint, doubledecimalcharulongfloatlong, nint, nuintoch uppräkningar.>=><=<

En relational_patterngäller för en typ T om en lämplig inbyggd binär relationsoperator definieras med båda operanderna av typen T, eller om det finns en explicit nullbar konvertering eller avboxningskonvertering från T till typen av konstantuttryck.

Det är ett kompileringsfel om uttrycket utvärderas till double.NaN, float.NaNeller en null-konstant.

När indatavärdet har en typ för vilken en lämplig inbyggd binär relationsoperator definieras, tas utvärderingen av operatorn som innebörden av relationsmönstret. Annars konverteras indatavärdet till typen av konstant uttryck med hjälp av en explicit nullbar eller avboxningskonvertering. Det är ett kompileringsfel om det inte finns någon sådan konvertering. Mönstret anses inte matcha om konverteringen misslyckas. Om konverteringen lyckas är resultatet av mönstermatchningsåtgärden resultatet av utvärderingen av uttrycket e «op» v där e är de konverterade indata, «op» är relationsoperatorn och v är det konstanta uttrycket.

Exempel:

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

De utdata som genereras är

Too high
Unknown
Acceptable

slutexempel

11.2.10 Logiskt mönster

En logical_pattern används för att negera resultatet av en mönstermatchning, eller för att kombinera resultatet av flera mönstermatchningar med hjälp av konjunktion (and) eller disjunction (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, andoch or kallas tillsammans för mönsteroperatorer.

Ett negated_pattern matchar om mönstret som negeras inte matchar, och vice versa. En conjunctive_pattern kräver att båda mönstren matchar. Ett disjunctive_pattern kräver endera mönstret för att matcha. Till skillnad från deras språkoperatormotsvarigheter och &&||, and och or är inte kortslutningsoperatorer.

Det är ett kompileringsfel för en mönstervariabel som ska deklareras under en not operator eller or mönsteroperator.

Obs! Eftersom varken not eller or kan generera en bestämd tilldelning för en mönstervariabel är det ett fel att deklarera en i dessa positioner. slutkommentar

I en conjunctive_pattern begränsas indatatypen för det andra mönstret av typen som begränsar kraven för det första mönstret i and. Den begränsade typen av ett mönster P definieras på följande sätt:

  • Om P är ett typmönster är den begränsade typen typen av typmönstertyp.
  • Annars, om P är ett deklarationsmönster , är den smala typen typen av deklarationsmönstrets typ.
  • Annars, om P är ett rekursivt mönster som ger en explicit typ, är den smala typen den typen.
  • Annars, om P matchas via reglerna för ITuple i en positional_pattern (§11.2.5), är den smala typen typen System.ITuple.
  • Annars, om P är ett konstant mönster där konstanten inte är null-konstanten och uttrycket inte har någon konstant uttryckskonvertering till indatatypen, är den begränsade typen typen av konstant.
  • Om P är ett relationsmönster där konstantuttrycket inte har någon konstant uttryckskonvertering till indatatypen är den begränsade typen typen av konstant.
  • Annars, om P är ett or mönster, är den smala typen den vanliga typen av den smala typen av undermönster om det finns en sådan gemensam typ. För detta ändamål tar den gemensamma typalgoritmen endast hänsyn till identitets-, boxnings- och implicita referenskonverteringar, och den tar hänsyn till alla undermönster i en sekvens av or mönster (ignorerar parenteserade mönster).
  • Annars, om P är ett and mönster, är den smala typen den smala typen av rätt mönster. Dessutom är den begränsade typen av det vänstra mönstret indatatypen för det högra mönstret.
  • Annars är Pden begränsade typen av P indatatyp.

Obs! Som indikeras av grammatiken not har företräde framför and, som har företräde framför or. Detta kan uttryckligen anges eller åsidosättas med parenteser. slutkommentar

När ett mönster visas på höger sida av isbestäms mönstrets omfattning av grammatiken, vilket innebär att mönsteroperatorerna and, oroch not inom mönstret binder hårdare än de logiska operatorerna &&, ||och ! utanför mönstret.

Exempel:

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

De utdata som genereras är

High
Too low
Acceptable

slutexempel

Exempel:

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

De utdata som genereras är

winter
autumn
spring

slutexempel

Exempel:

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

De utdata som genereras är

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

slutexempel

11.3 Undersummor för mönster

I en switch-instruktion är det ett fel om ett ärendes mönster undersummas av föregående uppsättning obevakade ärenden (§13.8.3). Informellt innebär detta att alla indatavärden skulle ha matchats av ett av de tidigare fallen. Följande regler definierar när en uppsättning mönster undersummar ett givet mönster:

Ett mönster Pskulle matcha en konstant K om något av följande villkor gäller:

  • specifikationen för det mönstrets körningsbeteende är att matchar PK.
  • P är en type_pattern för typen T och K är inte null och körningstypen K är T eller en typ som härleds från T eller en typ som implementerar T.
  • P är en relational_pattern med operatorn «op» och konstant v, och uttrycket K «op» v skulle utvärderas till true.
  • P är en negated_patternnot P₁ och P₁ skulle inte matcha K.
  • P är en conjunctive_patternP₁ and P₂ och båda P₁ matchar K och P₂ skulle matcha K.
  • P är en disjunctive_patternP₁ or P₂ och skulle antingen P₁ matcha K eller P₂ matcha K.
  • P är en discard_pattern.

En uppsättning mönster Qundersummar ett mönster P om något av följande villkor gäller:

  • P är ett konstant mönster och något av mönstren i uppsättningen Q skulle matcha Pdet konverterade värdet
  • Pär ett var-mönster och uppsättningen mönster är fullständig (§11.4) för typen av mönsterindatavärde (Q), och antingen är mönsterindatavärdet inte av en nullbar typ eller så matchar något mönster i .Qnull
  • P är ett deklarationsmönster med typ T och uppsättningen mönster Q är fullständig för typen T (§11.4).
  • P är en type_pattern för typen T och uppsättningen mönster Q är fullständig för typen T.
  • P är en relational_pattern med operatorn «op» och konstant värde v, och för varje värde av indatatypen som uppfyller relationen «op» vskulle ett mönster i uppsättningen Q matcha det värdet.
  • P är en disjunctive_patternP₁ or P₂ och en uppsättning mönsterundersummor QP₁ och Q undersummor P₂.
  • P är en conjunctive_patternP₁ and P₂ och minst ett av följande undantag: Q undersummor P₁eller Q undersummor P₂.
  • P är en negated_patternnot P₁ och Q är fullständig för indatatypen med tanke på de värden som inte matchas av P₁.
  • Pär en discard_pattern och uppsättningen mönster Q är fullständig för typen av mönsterindatavärde, och antingen är mönsterindatavärdet inte av en nullbar typ eller så matchar nullnågot mönster i Q .
  • Vissa mönster i Q är en disjunctive_patternQ₁ or Q₂ och att ersätta det mönstret med Q₁ i Q skulle ge en uppsättning som undersummar Peller ersätta den med Q₂ skulle ge en uppsättning som undersummor P.
  • Vissa mönster i Q är en negated_patternnot Q₁ och P skulle inte matcha något värde som Q₁ skulle matcha.

11.4 Mönster fullständighet

Informellt är en uppsättning mönster uttömmande för en typ om, för varje möjligt värde av den typen förutom null, något mönster i uppsättningen är tillämpligt. Följande regler definierar när en uppsättning mönster är uttömmande för en typ:

En uppsättning mönster Q är fullständig för en typ T om något av följande villkor gäller:

  1. T är en integrerad eller uppräkningstyp, eller en nullbar version av en av dessa, och för varje möjligt värde av Tden underliggande typen som inte kan nollföras skulle något mönster i Q matcha det värdet, eller
  2. Vissa mönster i Q är ett var-mönster, eller
  3. Ett mönster i Q är ett deklarationsmönster för typen D, och det finns en identitetskonvertering, en implicit referenskonvertering eller en boxningskonvertering från T till D; eller
  4. Vissa mönster i Q är en type_pattern för typen D, och det finns en identitetskonvertering, en implicit referenskonvertering eller en boxningskonvertering från T till D; eller
  5. Vissa mönster i Q är en discard_pattern, eller
  6. Mönstren i Q inkluderar en kombination av relational_patterns och constant_patternvars intervall tillsammans täcker alla möjliga värden för Tden underliggande typen som inte kan nollas. För float och double typer, detta inkluderar System.Double.NaN eller System.Single.NaN respektive, eftersom NaN inte matchas av något relationsmönster; eller
  7. Vissa mönster i Q är en disjunctive_patternP₁ or P₂, och att ersätta det mönstret med både P₁ och P₂ i Q ger en uppsättning som är fullständig för T; eller
  8. Vissa mönster i Q är en negated_patternnot P₁, och mönstren tillsammans Q med värdena som inte matchas av P₁ täcker alla möjliga värden för T. En negated_patternnot P₁ är fullständig i sig när P₁ den inte matchar något möjligt värde för T; eller
  9. Vissa mönster i Q är en conjunctive_patternP₁ and P₂, och uppsättningen som endast P₁ innehåller är fullständig för T och uppsättningen som endast P₂ innehåller är fullständig för T.

Obs! När ett typmönster innehåller nullbara typer kan mönstret vara fullständigt för typen men ändå generera en varning eftersom typmönstret inte matchar ett null värde. slutkommentar

Obs! För flyttalstyper är kombinationen av mönster < 0 och >= 0inte fullständig eftersom inget relationsmönster matchar NaN. En korrekt fullständig uppsättning skulle vara < 0, >= 0och double.NaN (eller float.NaN). slutkommentar

Exempel:

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

slutexempel