你一定会好奇“老周你去哪开飞机了这么久没写博客了。”老周“我买不起飞机开了个铁矿挖了一年半的石头。谁知铁矿垮了压死了几条蜈蚣什么也没挖着。”所以这么丢死人的事还是不要提了爷爷从小教导我做人要低调……一转眼.NET 5 要来了同时也带来了 C# 9。遥想当年老周刚接触 .NET 1.1 的时候才刚上大学如今已经过去13年了。岁月是把水果刀从来不饶人啊。老周很少去写诸如“XXX新特性”之类的文章总觉得没啥用处。不过针对 C# 9老周想说一点什么。好在开始之前老周再次强调一下这些语言新特性的东西你千万不要特意去学习千万不要不要不要重要的事情讲四遍这些玩意儿你只要看看官方给的说明刷一遍就能掌握了刷这个比刷抖音有意义多了不用去学的。如果你连这些东东也要学习成本的话我只想说句好唱不好听的话——你的学习能力真的值得怀疑。好了下面开始表演。第一出record 类型record 我还是用原词吧我知道有翻译为“记录类型”的说法。只是只是老周老觉得这不太好听可是老周也找不出更好的词语还是用回 record吧。record 是引用类型跟 class 很像确实差不多。那么用人民群众都熟悉的 class 不香吗为何要新增个 record 呢答为了数据比较的便捷。不明白没事往下看。最近有一位热心邻居送了老周一只宠物public class Cat { public string Nick { get; set; } public string Name { get; set; } public int Age { get; set; } }这只新宠物可不简单一顶一的高级吃货。鱼肉、猪肉、鸡腿、饼干、豆腐、面包、水果、面条、小麦、飞蛾……反正只要它能塞进嘴里的它都吃。接下来我们 new 两个宠物实例。// 两个实例描述的是同一只猫 Cat pet1 new Cat { Nick 松子, Name Jack, Age 1 }; Cat pet2 new Cat { Nick 松子, Name Jack, Age 1 }; // 居然不是同一只猫 Console.WriteLine(同一只{0}, pet1 pet2);其实两个实例描述的都是我家的乖乖。可是输出的是同一只False这是因为在相等比较时人家关心的类型引用——引用的是否为同一个实例。但是在数据处理方案中我们更关注对象中的字段/属性是否相等即内容比较。现在把 Cat 的声明改为 record 类型。public record Cat { public string Nick { get; set; } public string Name { get; set; } public int Age { get; set; } }然后同样用上面的 pet1 和 pet2 实例进行相等比较得到预期的结果同一只Truerecord 类型让你省去了重写相等比较重写 Equals、GetHashCode 等方法或重载运算符的逻辑。实际上代码在编译后 record 类型也是一个类但自动实现了成员相等比较的逻辑。以前你要手动去折腾的事现在全交给编译器去干。假如有一个 User 类型用于表示用户信息包括用户名、密码然后这个 User 类型在数据处理方案中可能会产生N多个实例。例如你根据条件从EF模型中筛选出一个 User 实例 A根据用户输入的登录名和密码产生了 User 实例 B。为了验证用户输入的登录信息是否正确如果 User 是 class你可能要这样判断if(A.UserName B.UserName A.Password B.Password) { .................. }但要是你把 User 定义为 record 类型那么一句话的工夫A B第二出模式匹配Pattern Matching模式匹配这个翻译感觉怪怪滴老周还没想出什么更好的词语。模式匹配并不是什么神奇的东西它只是在对变量值进行检测时的扩展行为。以前老感觉C/C# 的 switch 语句不够强大因为传统的用法里面每个 case 子句只能比较单个常量值。比如int 考试成绩 85; switch (考试成绩) { case 10: Console.WriteLine(才考这么点破分啊); break; case 50: Console.WriteLine(还差一点就合格了); break; case 85: Console.WriteLine(真是秀); break; case 90: Console.WriteLine(奇迹发生); break; }我幻想着要是能像下面这样写就好了switch (考试成绩) { case 0: Console.WriteLine(缺考); break; case 0 30: Console.WriteLine(太烂了); break; case 30 60: Console.WriteLine(还是不行); break; case 60 80: Console.WriteLine(还得努力); break; case 80 90: Console.WriteLine(秀儿真优秀); break; case 90 100: Console.WriteLine(不错奇迹); break; }等了很多年很多年“千年等一回等……”以后终于可以实现了。switch (考试成绩) { case 0: Console.WriteLine(缺考); break; case 0 and 30: Console.WriteLine(太烂了); break; case 30 and 60: Console.WriteLine(还是不行); break; case 60 and 80: Console.WriteLine(还得努力); break; case 80 and 90: Console.WriteLine(秀儿真优秀); break; case 90 and 100: Console.WriteLine(不错奇迹); break; }哟西真香。有时候不仅要检测对象的值还得深入到其成员。比如下面这个例子Order类表示一条订单信息。public class Order { public int ID { get; set; } public string Company { get; set; } public string ContactName { get; set; } public float Qty { get; set; } public decimal UP { get; set; } public DateTime Date { get; set; } }前不久公司接到一笔Order做成了收益应该不错。Order od new Order { ID 11, Company 大嘴狗贸易有限公司, ContactName 陈大爷, Qty 425.12f, UP 1000.55M, Date new(2020, 10, 27) };假如我要在变量 od 上做 switch看看就这样switch (od) { case { Qty: 1000f }: Console.WriteLine(发财了发财了); break; case { Qty: 500f }: Console.WriteLine(好家伙年度大订单); break; case { Qty: 100f }: Console.WriteLine(订单量不错); break; }咦这这是什么鬼莫惊莫惊这不是鬼。它的意思是判断 Qty 属性的值如果订单货量大于 100 就输出“订单量不错”要是订单货量大于 1000那就输出“发财了发财了”。但你会说这对大括号怎么来的呢还记得这种 LINQ 的写法吗from x in ... where x.A ... select new { Prop1 ..., Prop2 ..., ................ }new { ... } 是匿名类型实例那如果是非匿名类型呢看看前面的 Cat 实例初始化。Cat { .......... }这就对了这对大括号就是构造某实例的成员值用的所以上面的 switch 语句其实是这样写的switch (od) { case Order{ Qty: 1000f }: Console.WriteLine(发财了发财了); break; case Order{ Qty: 500f }: Console.WriteLine(好家伙年度大订单); break; case Order{ Qty: 100f }: Console.WriteLine(订单量不错); break; }Order{ ... } 就是匹配一个 Order 对象实例并且它的 Qty 属性要符合 ... 条件。由于变量 od 始终就是 Order 类型所以case 子句中的 Order 就省略了变成case { Qty: 1000f }: Console.WriteLine(发财了发财了); break;如果出现多个属性则表示为多个属性设定匹配条件它们之间是“且”的关系。比如case { Qty: 100f, Company: not null }: Console.WriteLine(订单量不错); break;猜猜啥意思这个是可以“望文生义”的Qty 属性的值要大于 100并且 Company 属性的值不能为 null。不为 null 的写法是 not null不要写成 !null因为这样太难看了。如果你的代码分支较少你可以用 if 语句的只是得配合 is 运算符。if (od is { UP: 3000M }) { Console.WriteLine(报价不理想); }但是这个写法目前有局限性它只能用常量值来做判断你要是这样写就会报错。if (od is { Date: DateTime.Now }) { ................ }DateTime.Now 不是常量值上面代码无法通过编译。