Tutorial: Usar os recursos de correspondência de padrões para estender tipo de dadosTutorial: Using pattern matching features to extend data types

O C#7 introduziu recursos básicos de correspondência de padrões.C# 7 introduced basic pattern matching features. Esses recursos foram estendidos no C# 8 com novos padrões e expressões.Those features are extended in C# 8 with new expressions and patterns. É possível escrever uma funcionalidade que se comporte como se você tivesse estendido tipos que poderiam estar em outras bibliotecas.You can write functionality that behaves as though you extended types that may be in other libraries. Outro uso dos padrões é criar a funcionalidade de que seu aplicativo precisa, mas que não é um recurso fundamental do tipo que está sendo estendido.Another use for patterns is to create functionality your application requires that isn't a fundamental feature of the type being extended.

Neste tutorial, você aprenderá a:In this tutorial, you'll learn how to:

  • Reconhecer situações em que a correspondência de padrões deverá ser usada.Recognize situations where pattern matching should be used.
  • Usar expressões de correspondência de padrões para implementar o comportamento com base em tipos e valores de propriedade.Use pattern matching expressions to implement behavior based on types and property values.
  • Combinar a correspondência de padrões com outras técnicas para criar algoritmos completos.Combine pattern matching with other techniques to create complete algorithms.

Pré-requisitosPrerequisites

Você precisará configurar seu computador para executar o .NET Core, incluindo o C# compilador 8,0.You’ll need to set up your machine to run .NET Core, including the C# 8.0 compiler. O C# compilador 8 está disponível a partir do Visual Studio 2019 versão 16,3 ou do SDK do .NET Core 3,0.The C# 8 compiler is available starting with Visual Studio 2019 version 16.3 or .NET Core 3.0 SDK.

Este tutorial pressupõe que você esteja familiarizado com o C# e .NET, incluindo o Visual Studio ou a CLI do .NET Core.This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or the .NET Core CLI.

Cenários para a correspondência de padrõesScenarios for pattern matching

O desenvolvimento moderno geralmente inclui a integração de dados de várias fontes e a apresentação de informações e insights de dados em um único aplicativo coeso.Modern development often includes integrating data from multiple sources and presenting information and insights from that data in a single cohesive application. Você e sua equipe não terão controle ou acesso a todos os tipos que representam os dados de entrada.You and your team won't have control or access for all the types that represent the incoming data.

O design orientado a objeto clássico exigiria a criação de tipos em seu aplicativo que representassem cada tipo de dados das várias fontes de dados.The classic object-oriented design would call for creating types in your application that represent each data type from those multiple data sources. O aplicativo, então, trabalharia com esses novos tipos, criaria hierarquias de herança, métodos virtuais e implementaria abstrações.Then, your application would work with those new types, build inheritance hierarchies, create virtual methods, and implement abstractions. Essas técnicas funcionam e, às vezes, são as melhores ferramentas.Those techniques work, and sometimes they are the best tools. Outras vezes, é possível escrever menos código.Other times you can write less code. Você pode escrever um código mais claro usando técnicas que separam os dados das operações que manipulam esses dados.You can write more clear code using techniques that separate the data from the operations that manipulate that data.

Neste tutorial, você vai criar e explorar um aplicativo que usa dados recebidos de várias fontes externas para um único cenário.In this tutorial, you'll create and explore an application that takes incoming data from several external sources for a single scenario. Você verá como a correspondência de padrões fornece uma maneira eficiente para consumir e processar esses dados de maneiras que não eram parte do sistema original.You'll see how pattern matching provides an efficient way to consume and process that data in ways that weren't part of the original system.

Imagine, por exemplo, uma área metropolitana principal que está implantando pedágios e preços diferenciados em horário de pico para gerenciar o tráfego.Consider a major metropolitan area that is using tolls and peak time pricing to manage traffic. Você escreve um aplicativo que calcula o pedágio de um veículo com base em seu tipo.You write an application that calculates tolls for a vehicle based on its type. Posteriormente, as melhorias vão incorporar preços com base no número de ocupantes do veículo.Later enhancements incorporate pricing based on the number of occupants in the vehicle. Outros aprimoramentos vão adicionar o preço com base na hora e no dia da semana.Further enhancements add pricing based on the time and the day of the week.

Com base nessa breve descrição, você pode ter elaborado rapidamente uma hierarquia de objetos para modelar esse sistema.From that brief description, you may have quickly sketched out an object hierarchy to model this system. No entanto, seus dados são provenientes de várias fontes, como outros sistemas de gerenciamento de registro do veículo.However, your data is coming from multiple sources like other vehicle registration management systems. Esses sistemas fornecem classes diferentes para modelar aqueles dados, e você não tem um modelo de objeto único o qual seja possível usar.These systems provide different classes to model that data and you don't have a single object model you can use. Neste tutorial, você usará essas classes simplificadas para criar o modelo para os dados do veículo, a partir desses sistemas externos, conforme mostrado no código a seguir:In this tutorial, you'll use these simplified classes to model for the vehicle data from these external systems, as shown in the following code:

namespace ConsumerVehicleRegistration
{
    public class Car
    {
        public int Passengers { get; set; }
    }
}

namespace CommercialRegistration
{
    public class DeliveryTruck
    {
        public int GrossWeightClass { get; set; }
    }
}

namespace LiveryRegistration
{
    public class Taxi
    {
        public int Fares { get; set; }
    }

    public class Bus
    {
        public int Capacity { get; set; }
        public int Riders { get; set; }
    }
}

Faça o download do código inicial no repositório dotnet/samples do GitHub.You can download the starter code from the dotnet/samples GitHub repository. É possível ver que as classes de veículos são de sistemas diferentes, e estão em namespaces diferentes.You can see that the vehicle classes are from different systems, and are in different namespaces. Nenhuma base comum de classe, além da System.Object pode ser aproveitada.No common base class, other than System.Object can be leveraged.

Designs de correspondência de padrõesPattern matching designs

O cenário usado neste tutorial destaca os tipos de problemas que a correspondência de padrões pode resolver de forma adequada:The scenario used in this tutorial highlights the kinds of problems that pattern matching is well-suited to solve:

  • Os objetos com os quais você precisa trabalhar não estão em uma hierarquia de objetos que corresponde aos seus objetivos.The objects you need to work with aren't in an object hierarchy that matches your goals. É possível que você esteja trabalhando com classes que fazem parte dos sistemas não relacionados.You may be working with classes that are part of unrelated systems.
  • A funcionalidade que você está adicionando não faz parte da abstração central dessas classes.The functionality you're adding isn't part of the core abstraction for these classes. A tarifa paga por um veículo muda de acordo com diferentes tipos de veículos, mas o pedágio não é uma função principal do veículo.The toll paid by a vehicle changes for different types of vehicles, but the toll isn't a core function of the vehicle.

Quando a forma dos dados e as operações nos dados não são descritas em conjunto, o recurso de correspondência padrões no C# facilita o trabalho.When the shape of the data and the operations on that data are not described together, the pattern matching features in C# make it easier to work with.

Implementar os cálculos básicos de pedágioImplement the basic toll calculations

O cálculo mais básico do pedágio dependerá apenas do tipo do veículo:The most basic toll calculation relies only on the vehicle type:

  • Um Car será R$2,00.A Car is $2.00.
  • Um Taxi será R$3,50.A Taxi is $3.50.
  • Um Bus será R$5,00.A Bus is $5.00.
  • Um DeliveryTruck será R$10,00.A DeliveryTruck is $10.00

Crie uma nova classe TollCalculator e implemente a correspondência de padrões no tipo de veículo para obter a quantidade do pedágio.Create a new TollCalculator class, and implement pattern matching on the vehicle type to get the toll amount. O código a seguir mostra a implementação inicial do TollCalculator.The following code shows the initial implementation of the TollCalculator.

using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;

namespace toll_calculator
{
    public class TollCalculator
    {
        public decimal CalculateToll(object vehicle) =>
            vehicle switch
        {
            Car c           => 2.00m,
            Taxi t          => 3.50m,
            Bus b           => 5.00m,
            DeliveryTruck t => 10.00m,
            { }             => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
            null            => throw new ArgumentNullException(nameof(vehicle))
        };
    }
}

O código anterior usa uma expressão switch (não igual a uma instrução switch) que testa o tipo de padrão.The preceding code uses a switch expression (not the same as a switch statement) that tests the type pattern. A expressão switch inicia-se com a variável, vehicle no código anterior, seguida pela palavra-chave switch.A switch expression begins with the variable, vehicle in the preceding code, followed by the switch keyword. Em seguida, estão os braços switch dentro de chaves.Next comes all the switch arms inside curly braces. A expressão switch faz outros refinamentos na sintaxe que circunda a instrução switch.The switch expression makes other refinements to the syntax that surrounds the switch statement. A palavra-chave case é omitida, e o resultado de cada braço é uma expressão.The case keyword is omitted, and the result of each arm is an expression. Os dois últimos braços apresentam um novo recurso de linguagem.The last two arms show a new language feature. O caso { } corresponde a qualquer objeto não nulo que não correspondia a um braço anterior.The { } case matches any non-null object that didn't match an earlier arm. Este braço captura qualquer tipo incorreto passado para esse método.This arm catches any incorrect types passed to this method. O caso { } precisa seguir os casos para cada tipo de veículo.The { } case must follow the cases for each vehicle type. Se a ordem for revertida, o caso { } terá precedência.If the order were reversed, the { } case would take precedence. Por fim, o padrão null detecta quando um null é passado para esse método.Finally, the null pattern detects when a null is passed to this method. O padrão null pode ser o último, porque os outros padrões de tipo correspondem apenas a um objeto não nulo do tipo correto.The null pattern can be last because the other type patterns match only a non-null object of the correct type.

Você pode testar esse código usando o seguinte código no Program.cs:You can test this code using the following code in Program.cs:

using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;

namespace toll_calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            var tollCalc = new TollCalculator();

            var car = new Car();
            var taxi = new Taxi();
            var bus = new Bus();
            var truck = new DeliveryTruck();

            Console.WriteLine($"The toll for a car is {tollCalc.CalculateToll(car)}");
            Console.WriteLine($"The toll for a taxi is {tollCalc.CalculateToll(taxi)}");
            Console.WriteLine($"The toll for a bus is {tollCalc.CalculateToll(bus)}");
            Console.WriteLine($"The toll for a truck is {tollCalc.CalculateToll(truck)}");

            try
            {
                tollCalc.CalculateToll("this will fail");
            }
            catch (ArgumentException e)
            {
                Console.WriteLine("Caught an argument exception when using the wrong type");
            }
            try
            {
                tollCalc.CalculateToll(null);
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("Caught an argument exception when using null");
            }
        }
    }
}

Esse código está incluído no projeto inicial, mas está comentado. Remova os comentários para poder testar o que acabou de escrever.That code is included in the starter project, but is commented out. Remove the comments, and you can test what you've written.

Você está começando a ver como os padrões podem ajudar a criar algoritmos em que o código e os dados estão separados.You're starting to see how patterns can help you create algorithms where the code and the data are separate. A expressão switch testa o tipo e produz valores diferentes com base nos resultados.The switch expression tests the type and produces different values based on the results. Mas isso é somente o começo.That's only the beginning.

Adicionar preços de acordo com a ocupação do veículoAdd occupancy pricing

A autoridade de pedágio deseja incentivar que os veículos viagem com a capacidade máxima de pessoas.The toll authority wants to encourage vehicles to travel at maximum capacity. Eles decidiram cobrar valores mais altos quando os veículos tiverem poucos passageiros e oferecer redução da tarifa para veículos com a capacidade total ocupada:They've decided to charge more when vehicles have fewer passengers, and encourage full vehicles by offering lower pricing:

  • Os carros e táxis com nenhum passageiro pagam uma taxa adicional de R$ 0,50.Cars and taxis with no passengers pay an extra $0.50.
  • Os carros e táxis com dois passageiros obtêm um desconto de R$ 0,50.Cars and taxis with two passengers get a $0.50 discount.
  • Os carros e táxis com três ou mais passageiros obtêm um desconto de R$ 1,00.Cars and taxis with three or more passengers get a $1.00 discount.
  • Os ônibus com menos de 50% da capacidade completa pagam uma taxa adicional de R$ 2,00.Buses that are less than 50% full pay an extra $2.00.
  • Os ônibus com 90% da capacidade de passageiros completa, ganham um desconto de R$ 1,00.Buses that are more than 90% full get a $1.00 discount.

Essas regras podem ser implementadas usando o padrão de propriedade na mesma expressão switch.These rules can be implemented using the property pattern in the same switch expression. O padrão de propriedade examina as propriedades do objeto depois que o tipo foi determinado.The property pattern examines properties of the object once the type has been determined. O único caso de um Car se expande para quatro casos diferentes:The single case for a Car expands to four different cases:

vehicle switch
{
    Car { Passengers: 0}        => 2.00m + 0.50m,
    Car { Passengers: 1 }       => 2.0m,
    Car { Passengers: 2}        => 2.0m - 0.50m,
    Car c                       => 2.00m - 1.0m,

    // ...
};

Os três primeiros casos testam o tipo como um Car, em seguida, verificam o valor da propriedade Passengers.The first three cases test the type as a Car, then check the value of the Passengers property. Se ambos corresponderem, essa expressão é avaliada e retornada.If both match, that expression is evaluated and returned.

Você também expande os casos para táxis de maneira semelhante:You would also expand the cases for taxis in a similar manner:

vehicle switch
{
    // ...

    Taxi { Fares: 0}  => 3.50m + 1.00m,
    Taxi { Fares: 1 } => 3.50m,
    Taxi { Fares: 2}  => 3.50m - 0.50m,
    Taxi t            => 3.50m - 1.00m,

    // ...
};

No exemplo anterior, a cláusula when foi omitida no caso final.In the preceding example, the when clause was omitted on the final case.

Em seguida, implemente as regras de ocupação expandindo os casos para os ônibus, conforme mostrado no exemplo a seguir:Next, implement the occupancy rules by expanding the cases for buses, as shown in the following example:

vehicle switch
{
    // ...

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus b => 5.00m,

    // ...
};

A autoridade de pedágio não está preocupada com o número de passageiros nos caminhões de carga.The toll authority isn't concerned with the number of passengers in the delivery trucks. Em vez disso, ela ajusta a quantidade de pedágios com base na classe de peso dos caminhões da seguinte maneira:Instead, they adjust the toll amount based on the weight class of the trucks as follows:

  • Os caminhões mais de 5000 quilos pagam uma taxa adicional de R$ 5,00.Trucks over 5000 lbs are charged an extra $5.00.
  • Os caminhões leves abaixo de 3.000 lb recebem um desconto de US$ 2,00.Light trucks under 3000 lbs are given a $2.00 discount.

Essa regra é implementada com o código a seguir:That rule is implemented with the following code:

vehicle switch
{
    // ...

    DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
    DeliveryTruck t => 10.00m,
};

O código anterior mostra a cláusula when de um braço switch.The preceding code shows the when clause of a switch arm. Você usa a cláusula when para testar as condições, com exceção da igualdade, em uma propriedade.You use the when clause to test conditions other than equality on a property. Ao terminar, o método será muito semelhante ao seguinte:When you've finished, you'll have a method that looks much like the following:

vehicle switch
{
    Car { Passengers: 0}        => 2.00m + 0.50m,
    Car { Passengers: 1}        => 2.0m,
    Car { Passengers: 2}        => 2.0m - 0.50m,
    Car c                       => 2.00m - 1.0m,

    Taxi { Fares: 0}  => 3.50m + 1.00m,
    Taxi { Fares: 1 } => 3.50m,
    Taxi { Fares: 2}  => 3.50m - 0.50m,
    Taxi t            => 3.50m - 1.00m,

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus b => 5.00m,

    DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
    DeliveryTruck t => 10.00m,
    
    { }     => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
    null    => throw new ArgumentNullException(nameof(vehicle))
};

Muitos desses braços switch são exemplos de padrões recursivos.Many of these switch arms are examples of recursive patterns. Por exemplo, Car { Passengers: 1} mostra um padrão constante dentro de um padrão de propriedade.For example, Car { Passengers: 1} shows a constant pattern inside a property pattern.

É possível fazer esse código menos repetitivo, usando switches aninhados.You can make this code less repetitive by using nested switches. O Car e Taxi têm quatro braços diferentes nos exemplos anteriores.The Car and Taxi both have four different arms in the preceding examples. Em ambos os casos, é possível criar um padrão de tipo que alimente um padrão de propriedade.In both cases, you can create a type pattern that feeds into a property pattern. Essa técnica é mostrada no código a seguir:This technique is shown in the following code:

public decimal CalculateToll(object vehicle) =>
    vehicle switch
    {
        Car c => c.Passengers switch
        {
            0 => 2.00m + 0.5m,
            1 => 2.0m,
            2 => 2.0m - 0.5m,
            _ => 2.00m - 1.0m
        },

        Taxi t => t.Fares switch
        {
            0 => 3.50m + 1.00m,
            1 => 3.50m,
            2 => 3.50m - 0.50m,
            _ => 3.50m - 1.00m
        },

        Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
        Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
        Bus b => 5.00m,

        DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
        DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
        DeliveryTruck t => 10.00m,

        { }  => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
        null => throw new ArgumentNullException(nameof(vehicle))
    };

Na amostra anterior, o uso de uma expressão recursiva significa não repetir os braços Car e Taxi contendo braços filho que testam o valor da propriedade.In the preceding sample, using a recursive expression means you don't repeat the Car and Taxi arms containing child arms that test the property value. Essa técnica não é usada para os braços Bus e DeliveryTruck porque esses estão testando intervalos da propriedade, e não valores discretos.This technique isn't used for the Bus and DeliveryTruck arms because those arms are testing ranges for the property, not discrete values.

Adicionar preço de horário de picoAdd peak pricing

Para um último recurso, a autoridade de pedágio quer adicionar um preço os horários de pico.For the final feature, the toll authority wants to add time sensitive peak pricing. Durante os horários de pico da manhã e do final da tarde, os pedágios serão dobrados.During the morning and evening rush hours, the tolls are doubled. Essa regra afetará apenas o tráfego em uma direção: entrada para a cidade, no período da manhã, e de saída da cidade, no período da tarde.That rule only affects traffic in one direction: inbound to the city in the morning, and outbound in the evening rush hour. Em outros períodos durante o dia útil, os pedágios aumentam 50%.During other times during the workday, tolls increase by 50%. Nos períodos da noite e madrugada e de manhã cedo, as tarifas são 25% mais baratas.Late night and early morning, tolls are reduced by 25%. Durante o fim de semana, a taxa é normal, independentemente da hora.During the weekend, it's the normal rate, regardless of the time.

Você usará a correspondência de padrões para esse recurso, mas poderá integrá-lo a outras técnicas.You'll use pattern matching for this feature, but you'll integrate it with other techniques. É possível criar uma única expressão de correspondência de padrões que leva em conta todas as combinações de direção, dia da semana e hora.You could build a single pattern match expression that would account for all the combinations of direction, day of the week, and time. O resultado seria uma expressão complicada.The result would be a complicated expression. Seria difícil de ler e entender.It would be hard to read and difficult to understand. O que dificulta garantir a exatidão.That makes it hard to ensure correctness. Em vez disso, combine esses métodos para criar uma tupla de valores que descreve de forma concisa todos os estados.Instead, combine those methods to build a tuple of values that concisely describes all those states. Em seguida, use a correspondência de padrões para calcular um multiplicador para o pedágio.Then use pattern matching to calculate a multiplier for the toll. A tupla contém três condições distintas:The tuple contains three discrete conditions:

  • O dia é um dia da semana ou do fim de semana.The day is either a weekday or a weekend.
  • A faixa de tempo é quando o pedágio é coletado.The band of time when the toll is collected.
  • A direção é para a cidade ou da cidadeThe direction is into the city or out of the city

A tabela a seguir mostra as combinações de valores de entrada e multiplicador de preços para os horários de pico:The following table shows the combinations of input values and the peak pricing multiplier:

DayDay TimeTime DirectionDirection PremiumPremium
Dia útilWeekday horário de pico da manhãmorning rush entradainbound x 2,00x 2.00
Dia útilWeekday horário de pico da manhãmorning rush saídaoutbound x 1,00x 1.00
Dia útilWeekday hora do diadaytime entradainbound x 1,50x 1.50
Dia útilWeekday hora do diadaytime saídaoutbound x 1,50x 1.50
Dia útilWeekday horário de pico do fim da tardeevening rush entradainbound x 1,00x 1.00
Dia útilWeekday horário de pico do fim da tardeevening rush saídaoutbound x 2,00x 2.00
Dia útilWeekday noite e madrugadaovernight entradainbound x 0,75x 0.75
Dia útilWeekday noite e madrugadaovernight saídaoutbound x 0,75x 0.75
Fim de semanaWeekend horário de pico da manhãmorning rush entradainbound x 1,00x 1.00
Fim de semanaWeekend horário de pico da manhãmorning rush saídaoutbound x 1,00x 1.00
Fim de semanaWeekend hora do diadaytime entradainbound x 1,00x 1.00
Fim de semanaWeekend hora do diadaytime saídaoutbound x 1,00x 1.00
Fim de semanaWeekend horário de pico do fim da tardeevening rush entradainbound x 1,00x 1.00
Fim de semanaWeekend horário de pico do fim da tardeevening rush saídaoutbound x 1,00x 1.00
Fim de semanaWeekend noite e madrugadaovernight entradainbound x 1,00x 1.00
Fim de semanaWeekend noite e madrugadaovernight saídaoutbound x 1,00x 1.00

Há 16 combinações diferentes das três variáveis.There are 16 different combinations of the three variables. Ao combinar algumas das condições, você simplificará a expressão switch.By combining some of the conditions, you'll simplify the final switch expression.

O sistema que coleta os pedágios usa uma estrutura DateTime para a hora em que o pedágio foi cobrado.The system that collects the tolls uses a DateTime structure for the time when the toll was collected. Construa métodos de membro que criam as variáveis da tabela anterior.Build member methods that create the variables from the preceding table. A seguinte função usa como correspondência de padrões a expressão switch para expressar se um DateTime representa um fim de semana ou um dia útil:The following function uses a pattern matching switch expression to express whether a DateTime represents a weekend or a weekday:

private static bool IsWeekDay(DateTime timeOfToll) =>
    timeOfToll.DayOfWeek switch
    {
        DayOfWeek.Monday    => true,
        DayOfWeek.Tuesday   => true,
        DayOfWeek.Wednesday => true,
        DayOfWeek.Thursday  => true,
        DayOfWeek.Friday    => true,
        DayOfWeek.Saturday  => false,
        DayOfWeek.Sunday    => false
    };

Esse método funciona, mas é redundante.That method works, but it's repetitious. Para simplificar, faça conforme mostrado no código a seguir:You can simplify it, as shown in the following code:

private static bool IsWeekDay(DateTime timeOfToll) =>
    timeOfToll.DayOfWeek switch
    {
        DayOfWeek.Saturday => false,
        DayOfWeek.Sunday   => false,
        _                  => true
    };

Depois, adicione uma função semelhante para categorizar o tempo nos blocos:Next, add a similar function to categorize the time into the blocks:

private enum TimeBand
{
    MorningRush,
    Daytime,
    EveningRush,
    Overnight
}

private static TimeBand GetTimeBand(DateTime timeOfToll)
{
    int hour = timeOfToll.Hour;
    if (hour < 6)
        return TimeBand.Overnight;
    else if (hour < 10)
        return TimeBand.MorningRush;
    else if (hour < 16)
        return TimeBand.Daytime;
    else if (hour < 20)
        return TimeBand.EveningRush;
    else
        return TimeBand.Overnight;
}

O método anterior não usa a correspondência de padrões.The previous method doesn't use pattern matching. Fica mais claro usando uma cascata familiar de instruções if.It's clearer using a familiar cascade of if statements. Adicione um enum privado para converter cada intervalo de tempo em um valor discreto.You do add a private enum to convert each range of time to a discrete value.

Depois de criar esses métodos, é possível usar outra expressão switch com o padrão de tupla para calcular o preço premium.After you create those methods, you can use another switch expression with the tuple pattern to calculate the pricing premium. Você pode construir uma expressão switch com todos os 16 braços:You could build a switch expression with all 16 arms:

public decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true,  TimeBand.MorningRush, true)  => 2.00m,
        (true,  TimeBand.MorningRush, false) => 1.00m,
        (true,  TimeBand.Daytime,     true)  => 1.50m,
        (true,  TimeBand.Daytime,     false) => 1.50m,
        (true,  TimeBand.EveningRush, true)  => 1.00m,
        (true,  TimeBand.EveningRush, false) => 2.00m,
        (true,  TimeBand.Overnight,   true)  => 0.75m,
        (true,  TimeBand.Overnight,   false) => 0.75m,
        (false, TimeBand.MorningRush, true)  => 1.00m,
        (false, TimeBand.MorningRush, false) => 1.00m,
        (false, TimeBand.Daytime,     true)  => 1.00m,
        (false, TimeBand.Daytime,     false) => 1.00m,
        (false, TimeBand.EveningRush, true)  => 1.00m,
        (false, TimeBand.EveningRush, false) => 1.00m,
        (false, TimeBand.Overnight,   true)  => 1.00m,
        (false, TimeBand.Overnight,   false) => 1.00m,
    };

O código acima funciona, mas pode ser simplificado.The above code works, but it can be simplified. Todas as oito combinações para o fim de semana têm o mesmo pedágio.All eight combinations for the weekend have the same toll. É possível substituir todas as oito pela seguinte linha:You can replace all eight with the following line:

(false, _, _) => 1.0m,

Tanto o tráfego de entrada quanto o de saída têm o mesmo multiplicador durante o dia e a noite, nos dias úteis.Both inbound and outbound traffic have the same multiplier during the weekday daytime and overnight hours. Esses quatro braços switch podem ser substituídos por estas duas linhas:Those four switch arms can be replaced with the following two lines:

(true, TimeBand.Overnight, _) => 0.75m,
(true, TimeBand.Daytime, _)   => 1.5m,

O código deverá ser semelhante ao seguinte após essas duas alterações:The code should look like the following code after those two changes:

public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true, TimeBand.MorningRush, true)  => 2.00m,
        (true, TimeBand.MorningRush, false) => 1.00m,
        (true, TimeBand.Daytime,     _)     => 1.50m,
        (true, TimeBand.EveningRush, true)  => 1.00m,
        (true, TimeBand.EveningRush, false) => 2.00m,
        (true, TimeBand.Overnight,   _)     => 0.75m,
        (false, _,                   _)     => 1.00m,
    };

Por fim, você pode remover os dois horários de pico em que é pago o preço normal.Finally, you can remove the two rush hour times that pay the regular price. Quando remover essas braços, substitua o false por um descarte (_) no braço switch final.Once you remove those arms, you can replace the false with a discard (_) in the final switch arm. O método concluído será o seguinte:You'll have the following finished method:

public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true, TimeBand.Overnight,   _)     => 0.75m,
        (true, TimeBand.Daytime,     _)     => 1.5m,
        (true, TimeBand.MorningRush, true)  => 2.0m,
        (true, TimeBand.EveningRush, false) => 2.0m,
        (_,    _,                    _)     => 1.0m,
    };

Este exemplo destaca uma das vantagens da correspondência de padrões: os branches de padrões são avaliados na ordem.This example highlights one of the advantages of pattern matching: the pattern branches are evaluated in order. Se você os reorganizar para que um branch anterior trate um dos casos posteriores, o compilador emitirá um aviso sobre o código inacessível.If you rearrange them so that an earlier branch handles one of your later cases, the compiler warns you about the unreachable code. Essas regras de linguagem tornam as simplificações anteriores mais fáceis com a certeza de que o código não foi alterado.Those language rules made it easier to do the preceding simplifications with confidence that the code didn't change.

A correspondência de padrões torna alguns tipos de código mais legíveis e oferece uma alternativa às técnicas orientadas a objeto quando não é possível adicionar o código às classes.Pattern matching makes some types of code more readable and offers an alternative to object-oriented techniques when you can't add code to your classes. A nuvem está fazendo com que os dados e a funcionalidade existam separadamente.The cloud is causing data and functionality to live apart. A forma dos dados e as operações nela não são necessariamente descritas juntas.The shape of the data and the operations on it aren't necessarily described together. Neste tutorial, você utilizou os dados existentes de maneiras completamente diferentes de sua função original.In this tutorial, you consumed existing data in entirely different ways from its original function. A correspondência de padrões proporcionou a capacidade de escrever a funcionalidade que substituiu esses tipos, ainda que não tenha sido possível estendê-los.Pattern matching gave you the ability to write functionality that overrode those types, even though you couldn't extend them.

Próximas etapasNext steps

Baixe o código concluído no repositório dotnet/samples do GitHub.You can download the finished code from the dotnet/samples GitHub repository. Explore os padrões por conta própria e adicione essa técnica em suas atividades regulares de codificação.Explore patterns on your own and add this technique into your regular coding activities. Aprender essas técnicas lhe oferece outra maneira de abordar problemas e criar novas funcionalidades.Learning these techniques gives you another way to approach problems and create new functionality.