Udostępnij za pośrednictwem


Dopasowywanie danych do wzorców

W tym samouczku pokazano, jak używać dopasowywania wzorców do sprawdzania danych w języku C#. Piszesz małe ilości kodu, a następnie kompilujesz i uruchamiasz ten kod. Samouczek zawiera serię lekcji, które eksplorują różne rodzaje typów w języku C#. Te lekcje nauczą Cię podstaw języka C#.

Wskazówka

Gdy blok fragmentu kodu zawiera przycisk "Uruchom", ten przycisk otwiera okno interakcyjne lub zastępuje istniejący kod w oknie interaktywnym. Gdy fragment kodu nie zawiera przycisku "Uruchom", możesz skopiować kod i dodać go do bieżącego okna interaktywnego.

W poprzednich samouczkach przedstawiono wbudowane typy i typy, które definiujesz jako krotki lub rekordy. Wystąpienia tych typów można sprawdzić względem wzorca. To, czy wystąpienie jest zgodne ze wzorcem, określa akcje, które wykonuje program. W poniższych przykładach zauważysz ? po nazwach typów. Ten symbol umożliwia, aby wartość tego typu mogła być null (na przykład wartość bool? może być true, false lub null). Aby uzyskać więcej informacji, zobacz Typy wartości dopuszczających null. Zacznijmy dowiedzieć się, jak można używać wzorców.

Dopasuj wartość

Wszystkie przykłady w tym samouczku wykorzystują dane tekstowe reprezentujące serię transakcji bankowych w formacie CSV (wartości rozdzielone przecinkami). W każdym z przykładów można dopasować rekord do wzorca przy użyciu is wyrażenia lub switch . Ten pierwszy przykład dzieli każdy wiersz na , znaku, a następnie pasuje do pierwszego pola ciągu względem wartości "DEPOZYT" lub "WYPŁATA" przy użyciu is wyrażenia. Gdy jest on zgodny, kwota transakcji jest dodawana lub odliczana z bieżącego salda konta. Aby zobaczyć, jak działa, naciśnij przycisk "Uruchom":

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

Sprawdź dane wyjściowe. Zobaczysz, że każdy wiersz jest przetwarzany, porównując wartość tekstu w pierwszym polu. Poprzednia próbka może być podobnie skonstruowana przy użyciu operatora == celu przetestowania, czy dwie string wartości są równe. Porównywanie zmiennej ze stałą jest podstawowym blokiem konstrukcyjnym do dopasowywania wzorców. Przyjrzyjmy się dokładniej blokom konstrukcyjnym, które są częścią dopasowywania wzorców.

Dopasowania wyliczenia

Kolejnym częstym zastosowaniem dopasowania do wzorca jest dopasowywanie wartości typu enum. W następnym przykładzie są przetwarzane rekordy wejściowe, aby utworzyć krotkę , w której pierwsza wartość jest wartością enum , która zwraca depozyt lub wypłatę. Druga wartość to wartość transakcji. Aby zobaczyć, jak działa, naciśnij przycisk "Uruchom":

Ostrzeżenie

Nie kopiuj i wklejaj. Aby uruchomić poniższe przykłady, należy zresetować okno interakcyjne. Jeśli popełnisz błąd, okno się zawiesza i musisz odświeżyć stronę, aby kontynuować.

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
}

W poprzednim przykładzie użyto if również instrukcji do sprawdzenia wartości enum wyrażenia. Inna forma dopasowywania wzorców używa switch wyrażenia. Przyjrzyjmy się tej składni i sposobom jej używania.

Wyczerpujące dopasowania z switch

Seria instrukcji if może przetestować serię warunków. Jednak kompilator nie może stwierdzić, czy seria instrukcji if jest wyczerpująca , czy późniejsze if warunki są subsumowane przez wcześniejsze warunki. Wyrażenie switch zapewnia spełnienie obu tych cech, co skutkuje mniejszą liczbą usterek w aplikacjach. Wypróbujmy to i poeksperymentujmy. Skopiuj następujący kod. W oknie interaktywnym zastąp dwie instrukcje if skopiowanym wyrażeniem switch. Po zmodyfikowaniu kodu naciśnij przycisk "Uruchom" w górnej części okna interaktywnego, aby uruchomić nowy przykład.

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

Po uruchomieniu kodu zobaczysz, że działa on tak samo. Aby zademonstrować podsumpcję, zmień kolejność ramion przełącznika, jak pokazano w poniższym fragmencie kodu:

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

Po zmianie kolejności ramion przełącznika naciśnij przycisk "Uruchom". Kompilator zgłasza błąd, ponieważ ramię z _ pasuje do każdej wartości. W rezultacie to ostatnie ramię z TransactionType.Withdrawal nigdy nie działa. Kompilator informuje, że coś jest nie tak w kodzie.

Kompilator wydaje ostrzeżenie, jeśli wyrażenie przetestowane w wyrażeniu switch może zawierać wartości, które nie pasują do żadnego ramienia przełącznika. Jeśli niektóre wartości mogą nie być zgodne z dowolnym warunkiem, switch wyrażenie nie jest wyczerpujące. Kompilator wyświetla również ostrzeżenie, jeśli niektóre wartości danych wejściowych nie pasują do żadnego z ramion przełącznika. Jeśli na przykład usuniesz wiersz z parametrem _ => 0.0,, wszystkie nieprawidłowe wartości nie są zgodne. W czasie wykonywania to się nie powiedzie. Po zainstalowaniu zestawu .NET SDK i kompilowania programów w środowisku możesz przetestować to zachowanie. Środowisko online nie wyświetla ostrzeżeń w oknie danych wyjściowych.

Wzorce typów

Aby ukończyć ten samouczek, przyjrzyjmy się jeszcze jednemu blokowi konstrukcyjnemu do dopasowywania wzorców: wzorcowi typu. Wzorzec typu testuje wyrażenie w czasie wykonywania, aby sprawdzić, czy jest to określony typ. Możesz użyć testu typu z wyrażeniem is lub wyrażeniem switch . Zmodyfikujmy bieżący przykład na dwa sposoby. Najpierw, zamiast krotki, skompilujmy Deposit i Withdrawal typy rekordów reprezentujące transakcje. Dodaj następujące deklaracje w dolnej części okna interaktywnego:

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

Następnie dodaj tę metodę po metodzie Main , aby przeanalizować tekst i zwrócić serię rekordów:

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

Na koniec zastąp pętlę foreach w metodzie Main następującym kodem:

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

Następnie naciśnij przycisk "Uruchom", aby wyświetlić wyniki. Ta ostateczna wersja testuje dane wejściowe względem typu.

Dopasowanie wzorca zapewnia słownictwo do porównywania wyrażenia z cechami. Wzorce mogą zawierać typ wyrażenia, wartości typów, wartości właściwości i ich kombinacje. Porównywanie wyrażeń ze wzorcem może być bardziej przejrzyste niż wiele if porównań. Poznaliśmy niektóre wzorce, których można użyć do dopasowywania wyrażeń. Istnieje wiele innych sposobów używania dopasowywania wzorców w aplikacjach. Najpierw odwiedź witrynę platformy .NET , aby pobrać zestaw .NET SDK, utworzyć projekt na maszynie i kontynuować kodowanie. Podczas eksplorowania możesz dowiedzieć się więcej na temat dopasowywania wzorców w języku C# w następujących artykułach: