好了,忙的时候结束了。
继续讲述和对象相关的知识。这一章讲使用模式匹配进行类型转换。
由于对象具有多态性。一个具有基类类型的变量是可以存放 derived 类型的变量的值的,但这有可能产生 InvallidCastException。C# 提供了使用模式匹配(pattern match)的格式转换(cast),仅当成功的时候会转换。C# 也提供了 is
和 as
关键字来判断一个值是否为某个类型。
1.1 is
运算符
比如说以下代码:
1 | C#复制代码static void FeedMammal(Animal a) { |
重点在于:
is
后面并不只是一个类型,而是声明了一个 Mammal 类型的变量。并不是说只能这么写。单单写a is Mammal
也行,只是这种语法把类型判断和初始化写在一起,也是可行的一种语法。当判断成功的时候,a 的值会被赋予给了 m。- m 的作用域仅仅在于 if 里,甚至连 else 里都无法访问。
1.2 as
运算符
请看以下代码:
1 | C#复制代码static void TestForMammals(Object o) { |
as
运算符执行一次转换。如果成功则转换成对应类型,不成功则返回 null。- 顺便一提,上面的
m != null
也可以换成m is not null
。
1.3 switch 做类型匹配
如下所示的语法也是可以的:
1 | C#复制代码static void PatternMatchingSwitch(System.ValueType val) |
现代开发经常要用到来自各种不同地方的数据源,因此数据类型也都不一致。
于是文章采用了这么一个场景–在一个收费站收费。根据高峰期和车型收费。
难点在于,数据来源可能是多个不同的外部系统。那么首先假设有这么三个系统(3 个 namespace):
1 | C#复制代码namespace ConsumerVehicleRegistration |
即,数据可能以不同的 class 形式存在。
2.1 最基础的收费
写一个最基础的收费类:
1 | C#复制代码using System; |
这里使用了一个 switch expression 的语法(非 switch statement)。语法一看大概也知道是怎么回事。因为整个是一个 switch,因此 => 跟的就是 return 的值。
- { } 则是匹配所有的 非 null 的 object。必须写在后面,否则就被第一个返回了。
- null 则是匹配 null。
2.2 根据乘客收费
为了减少流量,让车辆载客数更高,因此希望乘客越少收费越高。
我们可以改写上面的代码:
1 | C#复制代码public class TollCalculator |
when
的用法也是简洁明了。当并等于某一个值,而是一个判断语句的时候用 when。- 以上的代码有部分比较重复。比如对于 car 和 taxi,每个乘客数量都要写一整行代码。可以被简化为以下代码:
1 | C#复制代码public decimal CalculateToll(object vehicle) => |
- 可以看到根本没有新的语法。而是再写一个 switch expression。
- _ 表示匹配其他所有情况。同理也不能写在前面,因为一定会被匹配上。
2.3 根据高峰时间收费
假设有这么一个需求。周末正常收费。工作日的话,早上的入流量和晚上的出流量双倍收费。其他时间 1.5 倍收费。凌晨则减少为 0.75。
如果写成 if 语句,写倒是可以写,但是效果如下:
1 | C#复制代码public decimal PeakTimePremiumIfElse(DateTime timeOfToll, bool inbound) |
可以用,但非常难读,也不好改。
2.3.1 使用模式匹配以及其他技巧来简化代码
仅仅使用模式匹配来匹配所有可能性也不好,依然复杂,因为我们有很多种组合情况。
2.3.1.1 周末还是工作日
第一个条件是是否为周末。那么专门为此写一个函数:
1 | C#复制代码// 注意 timeOfToll.DayOfWeek 和 DayOfWeek.Monday 中的 DayOfWeek 不是一个东西。 |
还可以再简化:
1 | C#复制代码public static bool IsWeekday(DateTime timeOfToll) => |
2.3.1.2 一天的时间段
先看代码:
1 | C#复制代码public enum TimeBand |
- 使用了 enum 来将一天的多个时间段分配值。
- 使用了
> 19 or < 6
这种语法,>
和<
以及or
都是在 C# 9.0 后引入的。当然还有>=
,<=
,and
,not
这些语法。(什么你问为什么没有=
的语法,因为不需要,直接写 6 就是 =6 了)
2.3.1.3 最终代码
有了以上两个函数后,代码就可以简化为这种 tuple pattern 形式:
1 | C#复制代码public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => |
当然,很多条件可以简化:
1 | C#复制代码public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => |
然后可以把 3 个返回 1.00m 的用 _ 代替:
1 | C#复制代码public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => |
本文转载自: 掘金