Dela via


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.12.12) och i en switch_statement (§13.8.3) för att uttrycka formen på data som inkommande data ska jämföras med. Ett mönster testas mot uttrycket för en switch-instruktion, eller mot en relational_expression som finns till vänster om en operator, som var och en kallas för ettis.

11.2 Mönsterformulär

11.2.1 Allmänt

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

pattern
    : declaration_pattern
    | constant_pattern
    | var_pattern
    ;

En declaration_pattern och en var_pattern kan 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 inget 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.

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, ange värdet i en variabel av den typen.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : single_variable_designation
    ;
single_variable_designation
    : identifier
    ;

Körningstypen för värdet testas mot typen i mönstret med samma regler som anges i operatorn is-type (§12.12.12.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). 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önsterinmatningsvärde (§11.1) e, om simple_designation är identifierare_, anges ett ignorerande (§9.2.9.2) och värdet för 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 är någon annan identifierare introduceras en lokal variabel (§9.2.9.1) av den angivna typen med namnet 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 eller en avboxningskonvertering från E till T, eller om någon E av dem 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

Det är ett fel om typen är en nullbar värdetyp.

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

Med tanke på ett mönsterinmatningsvärde (§11.1) e, om beteckning är identifierare_, anger det en bortkastning (§9.2.9.2), och värdet för e är inte bundet till någonting. (Ä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.) Om beteckning är någon annan identifierare, är värdet för e bundet till en nyligen introducerad lokal variabel (§9.2.9.1) av det namnet vars typ är den statiska typen av eoch 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.

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 specifikationen för det mönstrets körningsbeteende är som P matchar K.

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

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. Vissa 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.

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