Matcha data mot mönster

I den här handledningen lär du dig hur du använder mönstermatchning för att inspektera data i C#. Du skriver små mängder kod och kompilerar och kör sedan koden. Självstudien innehåller en serie lektioner som utforskar olika typer av datatyper i C#. De här lektionerna lär dig grunderna i C#-språket.

Tips/Råd

När ett kodfragmentblock innehåller knappen "Kör" öppnas det interaktiva fönstret eller ersätter den befintliga koden i det interaktiva fönstret. När kodfragmentet inte innehåller någon "Kör"-knapp kan du kopiera koden och lägga till den i det aktuella interaktiva fönstret.

Föregående handledningar visade inbyggda typer och typer som du definierar som tupplar eller poster. Instanser av dessa typer kan kontrolleras mot ett mönster. Om en instans matchar ett mönster avgör vilka åtgärder programmet vidtar. I exemplen nedan kommer du att märka ? efter typnamn. Med den här symbolen kan värdet för den här typen vara null (till exempel bool? kan vara true, false eller null). Mer information finns i Nullable value types (Ogiltiga värdetyper). Nu ska vi börja utforska hur du kan använda mönster.

Matcha ett värde

Alla exempel i den här självstudien använder textindata som representerar en serie banktransaktioner som kommaavgränsade värden (CSV). I vart och ett av exemplen kan du matcha posten mot ett mönster med antingen ett is uttryck eller ett switch uttryck. Det första exemplet delar upp varje rad på , tecknet och matchar sedan det första strängfältet mot värdet "DEPOSIT" eller "WITHDRAWAL" med ett is uttryck. När den matchar läggs transaktionsbeloppet till eller dras av från det aktuella kontosaldot. Om du vill se att det fungerar trycker du på knappen "Kör":

string bankRecords = """
    DEPOSIT,   10000, Initial balance
    DEPOSIT,     500, regular deposit
    WITHDRAWAL, 1000, rent
    DEPOSIT,    2000, freelance payment
    WITHDRAWAL,  300, groceries
    DEPOSIT,     700, gift from friend
    WITHDRAWAL,  150, utility bill
    DEPOSIT,    1200, tax refund
    WITHDRAWAL,  500, car maintenance
    DEPOSIT,     400, cashback reward
    WITHDRAWAL,  250, dining out
    DEPOSIT,    3000, bonus payment
    WITHDRAWAL,  800, loan repayment
    DEPOSIT,     600, stock dividends
    WITHDRAWAL,  100, subscription fee
    DEPOSIT,    1500, side hustle income
    WITHDRAWAL,  200, fuel expenses
    DEPOSIT,     900, refund from store
    WITHDRAWAL,  350, shopping
    DEPOSIT,    2500, project milestone payment
    WITHDRAWAL,  400, entertainment
    """;

double currentBalance = 0.0;
var reader = new StringReader(bankRecords);

string? line;
while ((line = reader.ReadLine()) is not null)
{
    if (string.IsNullOrWhiteSpace(line)) continue;
    // Split the line based on comma delimiter and trim each part
    string[] parts = line.Split(',');

    string? transactionType = parts[0]?.Trim();
    if (double.TryParse(parts[1].Trim(), out double amount))
    {
        // Update the balance based on transaction type
        if (transactionType?.ToUpper() is "DEPOSIT")
            currentBalance += amount;
        else if (transactionType?.ToUpper() is "WITHDRAWAL")
            currentBalance -= amount;

        Console.WriteLine($"{line.Trim()} => Parsed Amount: {amount}, New Balance: {currentBalance}");
    }
}

Granska utdata. Du kan se att varje rad bearbetas genom att jämföra värdet för texten i det första fältet. Det föregående exemplet kan konstrueras på liknande sätt med operatorn == för att testa att två string värden är lika. Att jämföra en variabel med en konstant är en grundläggande byggsten för mönstermatchning. Nu ska vi utforska fler byggstenar som ingår i mönstermatchningen.

enum-träffar

En annan vanlig användning för mönstermatchning är att matcha värdena för en enum typ. I nästa exempel bearbetas indataposterna för att skapa en tupler där det första värdet är ett enum värde som noterar en insättning eller ett uttag. Det andra värdet är värdet för transaktionen. Om du vill se att det fungerar trycker du på knappen "Kör":

Varning

Kopiera och klistra inte in. Det interaktiva fönstret måste återställas för att köra följande exempel. Om du gör ett misstag låser sig fönstret och du måste uppdatera sidan för att fortsätta.

public static class ExampleProgram
{
    const string bankRecords = """
    DEPOSIT,   10000, Initial balance
    DEPOSIT,     500, regular deposit
    WITHDRAWAL, 1000, rent
    DEPOSIT,    2000, freelance payment
    WITHDRAWAL,  300, groceries
    DEPOSIT,     700, gift from friend
    WITHDRAWAL,  150, utility bill
    DEPOSIT,    1200, tax refund
    WITHDRAWAL,  500, car maintenance
    DEPOSIT,     400, cashback reward
    WITHDRAWAL,  250, dining out
    DEPOSIT,    3000, bonus payment
    WITHDRAWAL,  800, loan repayment
    DEPOSIT,     600, stock dividends
    WITHDRAWAL,  100, subscription fee
    DEPOSIT,    1500, side hustle income
    WITHDRAWAL,  200, fuel expenses
    DEPOSIT,     900, refund from store
    WITHDRAWAL,  350, shopping
    DEPOSIT,    2500, project milestone payment
    WITHDRAWAL,  400, entertainment
    """;

    public static void Main()
    {
        double currentBalance = 0.0;

        foreach (var transaction in TransactionRecords(bankRecords))
        {
            if (transaction.type == TransactionType.Deposit)
                currentBalance += transaction.amount;
            else if (transaction.type == TransactionType.Withdrawal)
                currentBalance -= transaction.amount;
            Console.WriteLine($"{transaction.type} => Parsed Amount: {transaction.amount}, New Balance: {currentBalance}");
        }
    }

    static IEnumerable<(TransactionType type, double amount)> TransactionRecords(string inputText)
    {
        var reader = new StringReader(inputText);
        string? line;
        while ((line = reader.ReadLine()) is not null)
        {
            string[] parts = line.Split(',');

            string? transactionType = parts[0]?.Trim();
            if (double.TryParse(parts[1].Trim(), out double amount))
            {
                // Update the balance based on transaction type
                if (transactionType?.ToUpper() is "DEPOSIT")
                    yield return (TransactionType.Deposit, amount);
                else if (transactionType?.ToUpper() is "WITHDRAWAL")
                    yield return (TransactionType.Withdrawal, amount);
            }
            else {
            yield return (TransactionType.Invalid, 0.0);
            }
        }
    }
}

public enum TransactionType
{
    Deposit,
    Withdrawal,
    Invalid
}

I föregående exempel används också en if -instruktion för att kontrollera värdet för ett enum uttryck. En annan form av mönstermatchning använder ett switch uttryck. Nu ska vi utforska syntaxen och hur du kan använda den.

Uttömmande matchningar med switch

En uppsättning if instruktioner kan testa en serie villkor. Kompilatorn kan dock inte avgöra om en serie if instruktioner är uttömmande eller om senare if villkor undersummas av tidigare villkor. Uttrycket switch säkerställer att båda dessa egenskaper uppfylls, vilket resulterar i färre buggar i dina appar. Vi provar det och experimenterar. Kopiera följande kod. Ersätt de två if uttrycken i det interaktiva fönstret med det switch uttryck som du kopierade. När du har ändrat koden trycker du på knappen "Kör" överst i det interaktiva fönstret för att köra det nya exemplet.

currentBalance += transaction switch
{
    (TransactionType.Deposit, var amount) => amount,
    (TransactionType.Withdrawal, var amount) => -amount,
    _ => 0.0,
};

När du kör koden ser du att den fungerar på samma sätt. Om du vill demonstrera subsumtion omorderar du switcharmarna enligt följande kodfragment.

currentBalance += transaction switch
{
    (TransactionType.Deposit, var amount) => amount,
    _ => 0.0,
    (TransactionType.Withdrawal, var amount) => -amount,
};

När du har ordnat om strömbrytarens armar trycker du på knappen "Kör". Kompilatorn ger ett fel eftersom armen med _ matchar alla värden. Därför kör förgreningen med TransactionType.Withdrawal aldrig. Kompilatorn säger att något är fel i koden.

Kompilatorn utfärdar en varning om uttrycket som testas i ett switch uttryck kan innehålla värden som inte matchar någon växelarm. Om vissa värden inte kan matcha något villkor switch är uttrycket inte uttömmande. Kompilatorn utfärdar också en varning om vissa värden för indata inte matchar någon av växelarmarna. Om du till exempel tar bort raden med _ => 0.0,matchar inte ogiltiga värden. Vid körning kommer det att misslyckas. När du har installerat .NET SDK och skapat program i din miljö kan du testa det här beteendet. Onlineupplevelsen visar inte varningar i utdatafönstret.

Typmönster

För att slutföra den här självstudien ska vi utforska ytterligare en byggsten för mönstermatchning: typmönstret. Ett typmönster testar ett uttryck under körning för att se om det är den specificerade typen. Du kan använda ett typtest med antingen ett is uttryck eller ett switch uttryck. Nu ska vi ändra det aktuella exemplet på två sätt. Först och främst, istället för en tuppel, ska vi skapa Deposit och Withdrawal rekordtyper som representerar transaktionerna. Lägg till följande deklarationer längst ned i det interaktiva fönstret:

public record Deposit(double Amount, string description);
public record Withdrawal(double Amount, string description);

Lägg sedan till här metoden efter Main metoden för att tolka texten och returnera en serie poster:

public static IEnumerable<object?> TransactionRecordType(string inputText)
{
    var reader = new StringReader(inputText);
    string? line;
    while ((line = reader.ReadLine()) is not null)
    {
        string[] parts = line.Split(',');

        string? transactionType = parts[0]?.Trim();
        if (double.TryParse(parts[1].Trim(), out double amount))
        {
            // Update the balance based on transaction type
            if (transactionType?.ToUpper() is "DEPOSIT")
                yield return new Deposit(amount, parts[2]);
            else if (transactionType?.ToUpper() is "WITHDRAWAL")
                yield return new Withdrawal(amount, parts[2]);
        }
        yield return default;
    }
}

Ersätt slutligen loopen foreach i metoden Main med följande kod:

foreach (var transaction in TransactionRecordType(bankRecords))
{
    currentBalance += transaction switch
    {
        Deposit d => d.Amount,
        Withdrawal w => -w.Amount,
        _ => 0.0,
    };
    Console.WriteLine($" {transaction} => New Balance: {currentBalance}");
}

Tryck sedan på knappen "Kör" för att se resultatet. Den här slutliga versionen testar indata mot en typ.

Mönstermatchning ger ett ordförråd för att jämföra ett uttryck med egenskaper. Mönster kan omfatta uttryckets typ, värden för typer, egenskapsvärden och kombinationer av dem. Att jämföra uttryck med ett mönster kan vara tydligare än flera if jämförelser. Du har utforskat några av de mönster som du kan använda för att matcha uttryck. Det finns många fler sätt att använda mönstermatchning i dina program. Gå först till .NET-webbplatsen för att ladda ned .NET SDK, skapa ett projekt på datorn och fortsätt att koda. När du utforskar kan du lära dig mer om mönstermatchning i C# i följande artiklar: