使用模式匹配来生成类型驱动和数据驱动的算法
题目:假设某大都市区通过通行费和高峰时段定价来管理交通。 你编写的应用程序根据车辆类型来计算车辆通行费。 后续增强功能包括,定价因车内乘客数而异。 进一步增强功能包括,定价因时间和周几而异。
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);
}
}

...