Freigeben über


Abgleichen von Daten mit Mustern

In diesem Lernprogramm erfahren Sie, wie Sie Musterabgleich verwenden, um Daten in C# zu prüfen. Sie schreiben kleine Codemengen, kompilieren und führen diesen Code aus. Das Tutorial enthält eine Reihe von Lektionen, die verschiedene Typen in C# erkunden. Diese Lektionen vermitteln Ihnen die Grundlagen der C#-Sprache.

Tipp

Wenn ein Codeausschnittblock die Schaltfläche "Ausführen" enthält, öffnet diese Schaltfläche das interaktive Fenster oder ersetzt den vorhandenen Code im interaktiven Fenster. Wenn der Codeausschnitt keine Schaltfläche "Ausführen" enthält, können Sie den Code kopieren und dem aktuellen interaktiven Fenster hinzufügen.

In den vorherigen Tutorials wurden eingebaute Typen und Typen veranschaulicht, die Sie als Tupel oder Datensätze definieren. Instanzen dieser Typen können anhand eines Musters überprüft werden. Ob eine Instanz mit einem Muster übereinstimmt, bestimmt die Aktionen, die Ihr Programm ausführt. In den folgenden Beispielen werden Sie ? nach Typnamen feststellen. Mit diesem Symbol kann der Wert dieses Typs null sein (zum Beispiel kann bool?true, false oder null sein). Weitere Informationen finden Sie unter Nullwertetypen. Sehen wir uns an, wie Sie Muster verwenden können.

Einen Wert abgleichen

In allen Beispielen in diesem Lernprogramm werden Texteingaben verwendet, die eine Reihe von Banktransaktionen als CSV-Eingabe (Comma Separated Values) darstellen. In jedem der Beispiele können Sie den Datensatz entweder mit einem is- oder switch-Ausdruck mit einem Muster abgleichen. In diesem ersten Beispiel wird jede Zeile anhand des , Zeichens aufgeteilt, und die erste Zeichenfolge wird mithilfe eines Ausdrucks mit dem Wert "DEPOSIT" oder "WITHDRAWAL" is. Bei Übereinstimmungen wird der Transaktionsbetrag vom aktuellen Kontostand hinzugefügt oder abgezogen. Um es arbeiten zu sehen, drücken Sie die Taste "Ausführen":

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

Prüfen Sie die Ausgabe. Sie können sehen, dass jede Zeile verarbeitet wird, indem Sie den Wert des Texts im ersten Feld vergleichen. Das vorherige Beispiel könnte ähnlich konstruiert werden, indem der == Operator verwendet wird, um zu testen, ob zwei string Werte gleich sind. Der Vergleich einer Variablen mit einer Konstante ist ein grundlegender Baustein für den Musterabgleich. Sehen wir uns mehr von den Bausteinen an, die Teil des Musterabgleichs sind.

Enum-Übereinstimmungen

Eine weitere häufige Verwendung für den Musterabgleich besteht darin, die Werte eines enum Typs abzugleichen. In diesem nächsten Beispiel werden die Eingabedatensätze verarbeitet, um ein Tupel zu erstellen, bei dem der erste Wert ein enum Wert ist, der eine Einzahlung oder eine Auszahlung angibt. Der zweite Wert ist der Wert der Transaktion. Um es arbeiten zu sehen, drücken Sie die Taste "Ausführen":

Warnung

Kopieren und fügen Sie nicht ein. Das interaktive Fenster muss zurückgesetzt werden, um die folgenden Beispiele auszuführen. Wenn Sie einen Fehler machen, hängt das Fenster, und Sie müssen die Seite aktualisieren, um fortzufahren.

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
}

Im vorherigen Beispiel wird auch eine if Anweisung verwendet, um den Wert eines enum Ausdrucks zu überprüfen. Eine andere Form des Musterabgleichs verwendet einen switch Ausdruck. Sehen wir uns diese Syntax an und wie Sie sie verwenden können.

Vollständige Übereinstimmungen mit switch

Eine Reihe von if Anweisungen kann eine Reihe von Bedingungen testen. Der Compiler kann jedoch nicht feststellen, ob eine Reihe von if Anweisungen erschöpfend ist oder ob spätere if Bedingungen von früheren Bedingungen subsumiert werden. Der switch Ausdruck stellt sicher, dass beide Merkmale erfüllt sind, was zu weniger Fehlern in Ihren Apps führt. Probieren wir es aus und experimentieren dabei. Kopieren Sie den folgenden Code. Ersetzen Sie die beiden if Anweisungen im interaktiven Fenster durch den Ausdruck switch, den Sie kopiert haben. Nachdem Sie den Code geändert haben, drücken Sie oben im interaktiven Fenster die Schaltfläche "Ausführen", um das neue Beispiel auszuführen.

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

Wenn Sie den Code ausführen, sehen Sie, dass er gleich funktioniert. Um die Subsumption zu veranschaulichen, ordnen Sie die Schalterarme neu an, wie im folgenden Codeschnipsel gezeigt:

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

Nachdem Sie die Schalterarme neu angeordnet haben, drücken Sie die Schaltfläche "Ausführen". Der Compiler gibt einen Fehler aus, weil der Arm mit _ mit jedem Wert übereinstimmt. Daher läuft dieser letzte Arm mit TransactionType.Withdrawal nie. Der Compiler teilt Ihnen mit, dass etwas in Ihrem Code falsch ist.

Der Compiler gibt eine Warnung aus, wenn der in einem switch Ausdruck getestete Ausdruck Werte enthalten kann, die keinem Schalterarm entsprechen. Wenn einige Werte nicht mit einer Bedingung übereinstimmen können, ist der switch Ausdruck nicht erschöpfend. Der Compiler gibt außerdem eine Warnung aus, wenn einige Werte der Eingabe keinem der Schalterarme entsprechen. Wenn Sie beispielsweise die Zeile mit _ => 0.0,entfernen, stimmen ungültige Werte nicht überein. Zur Laufzeit würde dies fehlschlagen. Nachdem Sie das .NET SDK installiert und Programme in Ihrer Umgebung erstellt haben, können Sie dieses Verhalten testen. Die Onlineumgebung zeigt keine Warnungen im Ausgabefenster an.

Typmuster

Um dieses Lernprogramm abzuschließen, untersuchen wir einen weiteren Baustein zum Musterabgleich: das Typmuster. Ein Typmuster testet einen Ausdruck zur Laufzeit, um festzustellen, ob es sich um den angegebenen Typ handelt. Sie können einen Typtest entweder mit einem is Ausdruck oder einem switch Ausdruck verwenden. Lassen Sie uns das aktuelle Beispiel auf zwei Arten ändern. Zuerst, anstatt eines Tupels, lassen Sie uns Deposit und Withdrawal Datensatztypen erstellen, die die Transaktionen darstellen. Fügen Sie unten im interaktiven Fenster die folgenden Deklarationen hinzu:

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

Fügen Sie als Nächstes diese Methode nach der Main Methode hinzu, um den Text zu analysieren und eine Reihe von Datensätzen zurückzugeben:

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

Ersetzen Sie schließlich die foreach Schleife in der Main Methode durch den folgenden Code:

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

Drücken Sie dann die Schaltfläche "Ausführen", um die Ergebnisse anzuzeigen. Diese endgültige Version testet die Eingabe anhand eines Typs.

Musterabgleich bietet ein Vokabular zum Vergleichen eines Ausdrucks mit Merkmalen. Muster können den Typ des Ausdrucks, Werte von Typen, Eigenschaftswerten und Kombinationen enthalten. Der Vergleich von Ausdrücken mit einem Muster kann deutlicher sein als mehrere if Vergleiche. Sie haben einige der Muster untersucht, die Sie zum Abgleichen von Ausdrücken verwenden können. Es gibt viele weitere Möglichkeiten zum Verwenden des Musterabgleichs in Ihren Anwendungen. Besuchen Sie zunächst die .NET-Website , um das .NET SDK herunterzuladen, ein Projekt auf Ihrem Computer zu erstellen und den Code beizubehalten. Während Sie erkunden, erfahren Sie mehr über den Musterabgleich in C# in den folgenden Artikeln: