使用模式匹配来生成类型驱动和数据驱动的算法

题目:假设某大都市区通过通行费和高峰时段定价来管理交通。 你编写的应用程序根据车辆类型来计算车辆通行费。 后续增强功能包括,定价因车内乘客数而异。 进一步增强功能包括,定价因时间和周几而异。

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

附加功能

using NUnit.Framework;

namespace Microsoft_learn._7_30;

public class TollCalculator
{
    /// <summary>
    /// 基础通行费计算
    /// </summary>
    /// <param name="vehicle">车辆类型</param>
    /// <returns></returns>
    /// <exception cref="ArgumentException">其他未统计车辆</exception>
    /// <exception cref="ArgumentNullException">参数为空的情况</exception>
    public decimal CalculateTollV1(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))
        };

    /// <summary>
    /// 通过乘客的数量来计算通行费
    /// </summary>
    /// <param name="vehicle"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    /// <exception cref="ArgumentNullException"></exception>
    public decimal CalculateTollV2(object vehicle) => vehicle switch
    {
        // Car {Passengers: 0}        => 2.00m + 0.50m,
        // Car {Passengers: 1}        => 2.0m,
        // Car {Passengers: 2}        => 2.0m - 0.50m,
        // Car                        => 2.00m - 1.0m,
        //
        // Taxi {Fares: 0}  => 3.50m + 1.00m,
        // Taxi {Fares: 1}  => 3.50m,
        // Taxi {Fares: 2}  => 3.50m - 0.50m,
        // Taxi             => 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 => 5.00m,
        //
        // DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
        // DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
        // DeliveryTruck => 10.00m,
        //
        // { }     => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
        // null    => throw new ArgumentNullException(nameof(vehicle))

        // 可使用嵌套的 switch 来减少此代码中重复的地方。 在上面的示例中,Car 和 Taxi 都有四个不同的臂。 在这两种案例中,都可创建向常量模式馈送数据的声明模式。 下面的代码展示了这项技术:
        Car car => car.Passengers switch
        {
            1 => 2.00m + 0.50m,
            2 => 2.00m,
            3 => 2.00m - 0.50m,
            _ => 2.00m - 1.00m
        },
        Taxi taxi => taxi.Fares switch
        {
            0 => 3.50m + 1.00m,
            1 => 3.50m + 0.50m,
            2 => 3.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 => 5.00m,

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

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

    public decimal CalculateTollV3(object vahicle)

    #region 附加功能 
    //对于最后一个功能,通行费收取机构希望添加有时效性的高峰时段定价。 在早晚高峰时段,通行费翻倍。 此规则只影响一个方向的交通:早高峰时段入城和晚高峰时段出城。
    //在工作日的其他时段,通行费增加 50%。 在深夜和清晨,通行费减少 25%。 在周末,无论什么时间,都按正常费率收费


    /// <summary>
    /// 判断当前时间是否为工作日
    /// </summary>
    /// <param name="dayOfWeek">时间</param>
    /// <returns></returns>
    public 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
            // 此方法虽然正确,但是具有重复性。 可以简化它
            DayOfWeek.Saturday => false,
            DayOfWeek.Sunday => false,
            _ => true
        };

    /// <summary>
    /// 时间分类枚举类
    /// </summary>
    private enum TimeBand
    {
        MorningRush,
        Daytime,
        EveningRush,
        Overnight
    };

    /// <summary>
    /// 判断当前时间属于哪个时间段
    /// </summary>
    /// <param name="time"></param>
    /// <returns></returns>
    private static TimeBand GetTimeBand(DateTime time) =>
        time.Hour switch
        {
            < 6 or > 19 => TimeBand.Overnight,
            < 10 => TimeBand.MorningRush,
            < 16 => TimeBand.Daytime,
            _ => TimeBand.EveningRush,
        };

    /// <summary>
    /// 根据时间和日期计算费用
    /// </summary>
    /// <param name="timeOfToll"></param>
    /// <param name="inbound"></param>
    /// <returns></returns>
    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,
            //上面的代码虽起作用,但可以进行简化。 周末对应的所有八个组合的通行费都相同
            // (false, _, _) => 1.00m,
            // (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,
            (true, TimeBand.MorningRush, true) => 2.00m,
            (true, TimeBand.Daytime, _) => 1.50m,
            (true, TimeBand.EveningRush, true) => 1.00m,
            (true, TimeBand.Overnight, _) => 0.75m,
            _ => 1.00m
        };

    
    #endregion
    [Test]
    public void Test01()
    {
        decimal calculateToll = CalculateTollV1(new Bus(1, 5));
        Console.WriteLine(calculateToll);
    }
}