Compartir a través de


Comparar datos contra patrones

En este tutorial se explica cómo usar la coincidencia de patrones para inspeccionar los datos en C#. Puede escribir pequeñas cantidades de código y, a continuación, compilar y ejecutar ese código. El tutorial contiene una serie de lecciones que exploran diferentes tipos de tipos en C#. Estas lecciones le enseñan los aspectos básicos del lenguaje C#.

Sugerencia

Cuando un bloque de fragmento de código incluye el botón "Ejecutar", ese botón abre la ventana interactiva o reemplaza el código existente en la ventana interactiva. Cuando el fragmento de código no incluye un botón "Ejecutar", puede copiar el código y agregarlo a la ventana interactiva actual.

En los tutoriales anteriores se demostraron los tipos integrados y los tipos que se definen como tuplas o registros. Las instancias de estos tipos se pueden comprobar con un patrón. Si una instancia coincide con un patrón determina las acciones que realiza el programa. En los ejemplos siguientes, observará ? después de los nombres de tipo. Este símbolo permite que el valor de este tipo sea NULL (por ejemplo, bool? puede ser true, false o null). Para obtener más información, consulte Tipos de valor anulable. Comencemos a explorar cómo puede usar patrones.

Igualar un valor

Todos los ejemplos de este tutorial usan la entrada de texto que representa una serie de transacciones bancarias como entrada de valores separados por comas (CSV). En cada una de las muestras, puede hacer coincidir el registro con un patrón utilizando una expresión de is o de switch. En este primer ejemplo, cada línea se divide en el carácter ,, y luego se compara el primer campo de cadena con el valor "DEPOSIT" o "WITHDRAWAL" utilizando una expresión is. Cuando coincide, el importe de la transacción se agrega o deduce del saldo de la cuenta actual. Para ver que funciona, presione el botón "Ejecutar":

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

Examine la salida. Puede ver que cada línea se procesa comparando el valor del texto en el primer campo. El ejemplo anterior podría construirse de forma similar mediante el == operador para probar que dos string valores son iguales. Comparar una variable con una constante es uno de los bloques básicos para la coincidencia de patrones. Vamos a explorar más de los bloques de construcción que forman parte de la coincidencia de patrones.

Coincidencias de enum

Otro uso común para la coincidencia de patrones es coincidir con los valores de un enum tipo. En este ejemplo siguiente se procesan los registros de entrada para crear una tupla donde el primer valor es un enum valor que anota un depósito o una retirada. El segundo valor es el valor de la transacción. Para ver que funciona, presione el botón "Ejecutar":

Advertencia

No copie y pegue. La ventana interactiva debe restablecerse para ejecutar los ejemplos siguientes. Si comete un error, la ventana se bloquea y necesita actualizar la página para continuar.

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
}

En el ejemplo anterior también se usa una if instrucción para comprobar el valor de una enum expresión. Otra forma de coincidencia de patrones usa una switch expresión. Vamos a explorar esa sintaxis y cómo se puede usar.

Coincidencias exhaustivas con switch

Una serie de if instrucciones puede probar una serie de condiciones. Pero el compilador no puede saber si una serie de if instrucciones son exhaustivas o si las if condiciones posteriores se subsumen en condiciones anteriores. La switch expresión garantiza que se cumplen ambas características, lo que produce menos errores en las aplicaciones. Vamos a probarlo y veamos qué pasa. Copie el código siguiente. Reemplaza las dos expresiones if de la ventana interactiva por la instrucción switch que copiaste. Después de modificar el código, presione el botón "Ejecutar" en la parte superior de la ventana interactiva para ejecutar el nuevo ejemplo.

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

Al ejecutar el código, verá que funciona igual. Para demostrar la subsumpción, reordene las ramas del interruptor como se muestra en el siguiente fragmento de código.

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

Después de reordenar los brazos del conmutador, presione el botón "Ejecutar". El compilador emite un error porque el brazo con _ coincide con cada valor. Como resultado, ese brazo final con TransactionType.Withdrawal nunca se ejecuta. El compilador le indica que algo está mal en el código.

El compilador emite una advertencia si la expresión probada en una switch expresión podría contener valores que no coinciden con ningún brazo de conmutador. Si algunos valores podrían no coincidir con ninguna condición, la switch expresión no es exhaustiva. El compilador también emite una advertencia si algunos valores de la entrada no coinciden con ninguno de los brazos switch. Por ejemplo, si quita la línea con _ => 0.0,, los valores no válidos no coinciden. En tiempo de ejecución, se produciría un error. Una vez que instale el SDK de .NET y compile programas en su entorno, puede probar este comportamiento. La experiencia en línea no muestra advertencias en la ventana de salida.

Patrones de tipo

Para finalizar este tutorial, exploremos un bloque más de construcción para la coincidencia de patrones: el patrón de tipo. Un patrón de tipo prueba una expresión en tiempo de ejecución para ver si es el tipo especificado. Puede usar una prueba de tipo con una is expresión o una switch expresión. Vamos a modificar el ejemplo actual de dos maneras. En primer lugar, en lugar de una tupla, vamos a construir tipos de registro Deposit y Withdrawal que representan las transacciones. Agregue las siguientes declaraciones en la parte inferior de la ventana interactiva:

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

A continuación, agregue este método después del Main método para analizar el texto y devolver una serie de registros:

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

Por último, reemplace el foreach bucle en el Main método por el código siguiente:

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

A continuación, presione el botón "Ejecutar" para ver los resultados. Esta versión final prueba la entrada contra un tipo.

La coincidencia de patrones proporciona un vocabulario para comparar una expresión con las características. Los patrones pueden incluir el tipo de la expresión, los valores de los tipos, los valores de propiedad y las combinaciones de ellas. Comparar expresiones con un patrón puede ser más claro que varias if comparaciones. Ha explorado algunos de los patrones que puede usar para hacer coincidir expresiones. Hay muchas más maneras de usar la coincidencia de patrones en las aplicaciones. En primer lugar, visite el sitio de .NET para descargar el SDK de .NET, crear un proyecto en la máquina y seguir codificando. A medida que explore, puede obtener más información sobre la coincidencia de patrones en C# en los siguientes artículos: