C#9特性整理(部分)

news/2024/7/25 21:34:13/文章来源:https://blog.csdn.net/abc1564984930/article/details/139110284

1. 实例化类型推断(Target-typed new)

我们会使用 new 关键字来实例化,但在部分字段和属性声明的时候,这些类型已经是在旁边给出,且不能使用 var 代替的。因此,我们必须这么写:

public Person XiaoMing { get; private set; } = new Person();

例如上面的类似写法。可以看到,实例化里必须要写上类型,但这个类型名称在上下文里已经给出,所以不必去写应该也知道,但 C# 一直没考虑省略的问题。直到 C# 9,这个特性才提上日程:

public Person XiaoMing { get; private set; } = new();

比如这样。

2. 基于平台的整型(Native integers)

C# 提供了两个新的关键字 nint 和 nuint 用来表达底层平台的 int 不确定(随机器位数大小变化的 int 类型)。

因为 C# 的 int 和 uint 都是固定的 4 字节数据,所以是不可变的。当我们不得不转换到底层(调用底层函数)的时候。

3. 模式匹配 3.0(Pattern matching improvements)

从 C# 7 开始,C# 开始支持模式匹配。模式匹配说白了就是基于数据的具体类型进行分情况讨论的一种用法。当一个类型需要从模糊转具体的时候,就需要模式匹配来判别具体的类型。C# 早期有 is 可以判断数据类型,有 as 可以进行尝试转换。但 C# 一直觉得这些写法还不够精简,所以发明了模式匹配的格式。

来看看 C# 每一个版本的模式匹配吧:

  • 模式匹配 1.0(C# 7):类型判别和转换一条蛇服务:o is T t、null 校验:o is null、switch 匹配类型;
  • 模式匹配 2.0(C# 8):类型属性和字段匹配模型(递归模式):sunnie is { Age: 24, Gender: 'm' }、switch 表达式风格的类型匹配。

C# 9 开始添加两种新的模式匹配模型:and/or/not 匹配模型和数据范围校验。

(1)and/or/not 模型

C# 新增了三个关键字:and、or 和 not。它们专门用来检测和校验数据的具体类型,以及数值是否是正常结果。与此同时,也可以校验递归模式。

因为用法比较多,所以这里就阐述其中一部分的用法:

bool b1 = o is Person and { Age: 24 } sunnie;
if (b1)
{bool b2 = sunnie is { Age: 24 } or { Gender: 'm' };if (b2){bool b3 = sunnie is { Age: not 24 };bool b4 = sunnie is not null;bool b5 = sunnie is not ({ Age: 80 } or { Gender: 'f'});}
}

(耸肩)看不懂对吧。

其实也不难。b1 是判断 o 是不是 Person 类型的。如果是,再看 Age 是不是 24。如果是,sunnie 才被正常转换过来。换句话说,b1 为 true 的时候,sunnie 变量必定有值(不是未赋值状态),且 Age 属性肯定是 24 这个结果。

b2 是看 sunnie 的 Age 是 24 或者 Gender 是 'm'。只要满足至少其中一个,就可以了。

b3 是看 sunnie 的 Age 不是 24 才满足要求,bool 才是 true,b4 是看 sunnie 不是 null 才满足要求,而 b5 是看 sunnie 的 Age 既不是 80,Gender 也不能是 'f',b5 才为 true。其中 b5 最复杂。不过你可以用数学上的取反来看:“非 A 且 非 B”的等价写法是“非 (A 或 B)”,所以 b5 也可以这么写:

bool b5 = sunnie is not { Age: 80 } and not { Gender: 'f' };

bool b5 = sunnie is { Age: not 80 } and { Gender: not 'f' };

但是需要你注意的是,模式匹配所比较的数值都必须是常量,即这里的 24、80、'f' 等。

稍微复杂一点的就是看 not 的推断了。我们可以认为,用大括号或者小括号一起判断的内容是且的关系,但对这样的条件取反就比较难理解了:

bool b = a is not { Prop1: < 20, Prop2: >= 40 };

这样的条件意味着什么呢?我们拆解语句,先把 not 改成可以看的办法:

bool b = !(a is { Prop1: < 20, Prop2: >= 40 });

然后取反。

bool b = a.Prop1 >= 20 || a.Prop2 < 40;

这里稍微注意一点的是,大括号连接表示 and,在取反时将改为 or,所以需要分开写。

(2)数值范围校验

除了这些判断方式外,C# 9 还允许在不知道数据类型的情况下对数据大小进行校验:

这个就表示,当 o 是 int 且范围是 10 到 20 之间、或是 float 且范围是 (float)10 到 (float)20 之间、或是 decimal 且范围是 (decimal)10 到 (decimal)20 之间,这三种情况之一,b 就为 true。

bool b = o is> 10 and < 20 or> 10F and < 20F or> 10M and < 20M;

(3)or 模式不能把类型校验变量内联到 is 校验里

我们刚才的 and 内联就可以将条件成立后的结果直接写到校验表达式 is 的最后。实际上 not 也可以:

if (o is not int i) { // ... } else { // Code can using 'i'. }

因为它完全等价于

if (!(o is int i)) { // ... } else { // Code can using 'i'. }

4. 静态 Lambda(Static lambdas)

当 Lambda 表达式不捕获数据的时候,我们允许使用 static 修饰 Lambda,来确保以后修改 API 和代码的时候,该 Lambda 不能捕获任何外部的数据。

Func<Person, int> selector = static (person) => person.Age;

另外,它也可以应用于静态的匿名函数上。

Func<Person, int> selector = static delegate(Person person)
{return person.Age;
}

这一点就是从 C# 8 出现的静态本地函数那边学来的。

5. 记录类型(Records)

(1)基本用法

记录类型是一种特殊的引用类型,它用来简化我们书写代码的代码量,同时达到了灵活程度。假如我们要定义一个轻量级的类型,这个类型就用来记录数据的结果信息的时候,我们就可以使用记录。如果我们这个类型假设带有 A、B、C 和 D 四个属性,我们直接写 A、B、C 和 D 要写四遍属性,然后初始化的时候要给这个类型传四个参数的构造器,对应赋值到这四个属性上;而且如果我们要重写 ToString 和一些比较函数的操作的时候,还得自己写。

这太麻烦了吧!所以 C# 9 里就简化了这些步骤,我们直接用一个 record 关键字就可以了:

public sealed record R(int A, int B, int C, int D);

你看,这么写是不是很简单?在编译后,R 会展开,把小括号里的四个参数直接输出变为属性,R 也会直接改成 public sealed class R,里面包含了很多已经帮你写好了的方法:ToString、Deconstruct 和 Clone 等等。

如果你不想要这些已经自动实现好的方法,你可以自己手写,不过请注意,Clone 方法不能重写,它在记录类型里有特殊用途,这一点我们在之后的特性 with 表达式里讲。

就像上面这样,把自动属性写在小括号里,这种我们叫做对位记录(Positional record)。

更广泛地,对位记录可以没有自动属性,所以小括号里没东西,所以干脆就不让你写小括号了,所以直接这么写:

public sealed record R;

写了后面的空小括号反而会报错,告诉你:一个对位记录必须带有至少一个自动属性在小括号里。

(2)记录的继承关系

就现在来说,记录类型只能继承自一个记录。但最终被翻译为 class 的缘故,也有很多声音想要让团队去支持 record 继承自普通类的,或者普通类继承自 record 的,不过这一点我们只能耐心等待了。

(3)它能做哪些用?

如果你把它当成一个普通的类的话,我想你应该就懂了:只要类能做的,它基本上都能做,而且还能简化书写的代码量,唯独就是这里的 Clone 你不能重写。记录类型甚至都把 operator == 和 operator != 都给你重写好了。

因为是简化类类型的书写才有的记录类型,所以它支持 sealed、abstract 等绝大多数修饰符,但 static 不行。因为 static class 用来提供静态操作的,但记录类型不是,它专门记录一个执行操作的效果,封装一个结果类型才出现的,所以没有 static record。

6. with 表达式(with expression)

(1)基本用法

为了解释记录的原理,C# 专门提供了两个语法用来表示它们,一个叫 with 表达式,一个是 init 赋值器。我们先来说一下这个语法。

with 用来拓展一个记录,使得新的记录可以独立于原类型,并且在此之上更改和增加数据成员的数值:

Person p = new() { Name = "XiaoMing", Age = 24 };
Person q = p with { Name = "XiaoWang", IsGeBi = true };

我相信你可以看到这段代码不用解释就能理解意思。

对!就是直接给 q 赋值 p 的所有数据后,在此之上更新 Name 和 IsGeBi 的数值信息。

(2)with 的原理

我很高兴看到了这个语法机制,但我不得不说一下它的具体执行行为。with 关键字在后面跟上一个对象初始化器,以完成对新的数据的更新。当然,没有更新的数据就保持原本的数值,比如这里的 q 的 Age 还是 24。那么它是怎么实现的呢?

还记得前面点到了一个内容吗?记录会默认实现一个不可修改的方法 Clone。这个方法为啥不可修改呢?因为这个方法是用来给 with 用的。换句话说,记录类型也是一个鸭子类型,只要你实现了 Clone 方法就能用 with,因为内部会调用 Clone 方法后,才会修改掉那些具体的数值。如果 C# 让你修改 Clone,整个执行操作的意义都变化了,以至于 with 的意义也发生了变化。C# 为了避免你这么做,就定了这么一个语法盐,不让你去动 Clone。

7. init 赋值器(init setter)

(1)基本用法

第二个用来理解记录类型不可变的语法就是这个 init 关键字了。init 和 get 还有 set 这两个关键字被定义成完全平级的关键字,你可以当成一个特殊的 set 块,只是这个块在初始化就用得上一次以外就不可再修改了。比如说

public class Person
{public char Gender { get; init; }public int Age { get; init; }
}

这个和 set 的使用语法相当类似,初始化器也能用:

var p = new Person { Gender = 'm', Age = 24 };

但你再次修改就不行了:

p.Age = 25; // Wrong.

这一点和普通类的 get 加 set 有区别。

另外,既然 set 和 init 都是用作赋值行为(作为赋值器),所以不能同时出现,一次就只能有一个。

(2)那么它和 get-only 的属性有啥区别呢?

前文说了 get; init; 组合和 get; set; 组合的区别,下面来说一下 get; init; 和 get; 的区别。

get; init; 可以让属性赋初始值,而 get; 也可以。举个例子:

public class A
{public A(int age) => Age = age; // Correct.public int Age { get; }
}
​
public class B
{public B(int age) => Age = age; // Correct.public int Age { get; init; }
}

这么来看,它们是没有区别的。唯一的区别就是初始化赋值的时候。前者只有 get 所以不让用对象初始化器;但后者就不一样了,后者保证了对象可以在初始化的时候赋值一次,而这个“初始化”可以通过构造器本身,也可以对象初始化器。这一点来说,只有 get 的属性是不能用对象初始化器的。

(3)init 是咋实现的?

嗯哼,这个底层原理可能会让你很失望,因为……确实很简单。

一个 get; set; 组合的属性,在底层被翻译成了一个字段、一个取值方法和一个赋值方法。它的代码是这样的:

private int _age;
​
public int Age
{get => _age;set => _age = value;
}

而 init 的呢?

private readonly int _age;
​
public int Age
{get => _age;set => _age = value;
}

发现区别了吗?对了,底层字段多了一个 readonly 而已。

这就很奇怪了朋友们。这样的话 set 依然是随意赋值的,那它是怎么保证你在其他地方随意赋值的时候报警告信息的?

答案其实很简单。带 readonly 的内部字段,那么 set 就可以改成 init,因为 readonly 字段保证了字段只在初始化的时候提供修改和赋值,之后就一直保持不变。这不是就是完全类似于 init 的行为吗?

是的,要说区别呢,就只有一个,readonly 是只能在构造器里赋值;而 init 可以在初始化器里,也可以在构造器里。不知道你想过一个问题没有,有没有 public readonly 组合?有对吧,那么你可不可能从外界来为这个对象赋值呢?显然就不可能了。因为它们都是在类内部初始化的,要么在它旁边直接赋值,要么就是构造器了。

那么,还是没解释怎么防止赋值的啊。实际上,这一点是从编译器层面搞定的。它检测你的赋值行为,是在初始化器里还是其他别的什么地方。只要是别的地方,肯定就是不允许的,自然就会报错了。

(4)啥时候用 init 呢?

你去思考一下,init 是为了配合记录类型才出现的一种语法,这也就意味着它在记录类型里就可以体现出非常神奇的语义。

记录是用来存储一些结果数据信息的,那么这些数据肯定是单纯用于显示和输出的,所以在初始化后它们肯定不能修改,即不可变。所以,在我们平时的编码过程中,只要对象专门用来表示初始化后不再变更的行为,就可以用 init 来替代 set。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1054608.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

总是不能盈利?试着用这两个观点去学习现货白银的技巧

一进入现货白银市场&#xff0c;投资者都想着如何去找到现货白银交易的机会&#xff0c;学习现货白银投资的方法。其实这些都是手段&#xff0c;而最终的目的还是为我们的盈利服务。而对于盈利来说&#xff0c;其实胜率和风险报酬比才是影响盈利的重要因素&#xff0c;我们带着…

设计软件有哪些?建模和造型工具篇(4),渲染100邀请码1a12

建模使用到的工具有很多&#xff0c;这次我们接着介绍。 1、PolyBoost PolyBoost是由Digimation公司开发的3ds Max插件&#xff0c;旨在增强软件的多边形建模功能。该插件提供了一系列强大的建模工具&#xff0c;如边缘控制、顶点编辑、面片调整等&#xff0c;使用户能够更加…

短视频脚本创作的五个方法 沈阳短视频剪辑培训

说起脚本&#xff0c;我们大概都听过影视剧脚本、剧本&#xff0c;偶尔可能在某些综艺节目里听过台本。其中剧本是影视剧拍摄的大纲&#xff0c;用来指导影视剧剧情的走向和发展&#xff0c;而台本则是综艺节目流程走向的指导大纲。 那么&#xff0c;短视频脚本是什么&#xf…

每日一题《leetcode--59.螺旋矩阵 》

https://leetcode.cn/problems/spiral-matrix-ii/ 这道题跟我昨天发布的那道题一模一样&#xff0c;只需要注意这个矩阵是n*n。 文章代码如下&#xff1a; int** generateMatrix(int n, int* returnSize, int** returnColumnSizes) {int** array (int**)malloc(sizeof(int*) *…

加密资产私钥安全完整手册(一) ,bitget钱包为例

比特币和以太坊等加密货币的兴起开创了数字金融的新时代&#xff0c;但也带来了独特的安全挑战。这些代表现实世界价值的数字资产已成为黑客和窃贼的主要目标。为了安全地应对这种情况&#xff0c;了解私钥的基本概念至关重要。 私钥是加密货币所有权和安全性的基石。它们相当于…

完成商品SPU管理页面

文章目录 1.引入前端界面1.将前端界面放到commodity下2.创建菜单3.进入前端项目&#xff0c;使用npm添加依赖1.根目录下输入2.报错 chromedriver2.27.2的问题3.点击链接下载压缩包&#xff0c;然后使用下面的命令安装4.再次安装 pubsub-js 成功5.在main.js中引入这个组件 4.修改…

真实故障分享,H3C ER3208G3-X路由器-双绞线一闪一停

六类非屏蔽双绞线 网线钳 如上图所示&#xff0c;2号线接到h3c路由器出现网线一闪一停&#xff0c;用对线器测试一到8芯能一一对应&#xff0c;无法上网。2号线接到h3c交换机能正常上网&#xff0c;难道是网线对568A 568B有要求&#xff1f; 解决方式&#xff1a;通过两端568…

本地开发正常 线上CI/CD构建项目过程报错文件未能正确引用

问题快照 原因分析&#xff1a; 一般遇到这样的错误就是 文件路径或者文件名称未能正确匹配 或者文件不存在 会报这样的错误 以为很好解决 但这次 都排查 了 就是 没发现原因 不管怎么说还是要感谢 GPT的能力(分析问题的能力) 先上图 当我看到 第四步的时候 我立马 去仓库里查…

大模型预训练结果到底是什么?

近日参加一个线下 AI 交流会议&#xff0c;会上有个非本行业的老师提问&#xff1a;“大家说的训练好的大模型到底是什么&#xff1f;是像 Word 软件一样可以直接使用的程序吗&#xff1f;” 这个问题看似简单&#xff0c;却一下把我问住了。的确&#xff0c;我们这些身处 AI 领…

POLARDB:新零售用户MySQL上云最佳选择

什么是云数据库POLARDB&#xff1f; POLARDB是阿里云自主研发的最新一代RDS关系型数据库&#xff0c;是特别针对互联网场景设计的Cloud-Native 云原生数据库。POLARDB for MySQL版本&#xff0c;在提供100%兼容MySQL5.6/8.0的关系型事务处理ACID特性之上&#xff0c;能够提供完…

算法之背包问题

可分的背包问题是可以用贪心法来解决&#xff0c;而0-1背包问题通常使用动态规划方法来解决。 可分背包问题&#xff1a; 在可分背包问题中&#xff0c;物品可以被分割&#xff0c;您可以取走物品的一部分以适应背包的容量。这里的关键是物品的价值密度&#xff0c;即单…

间接平差——以水准网平差为例 (python详细过程版)

目录 一、原理概述二、案例分析三、代码实现四、结果展示本文由CSDN点云侠原创,间接平差——以水准网平差为例 (python详细过程版),爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、原理概述 间接平差的函数模型和随机模型…

py黑帽子学习笔记_scapy

简介 代码简洁&#xff1a;相比于前两个博客总结&#xff0c;很多socket操作&#xff0c;如果使用scapy仅需几行代码即可实现 获取邮箱身份凭证 编写基础嗅探器&#xff0c;脚本可显示任何收到的一个包的详细情况 直接运行 尝试监听邮件收发&#xff0c;监听指定端口&#x…

解密网络流量监控:优化IT运维的利器

引言&#xff1a; 在当今数字化时代&#xff0c;网络流量监控是维护网络稳定与业务连续性的关键。作为一名资深网络工程师&#xff0c;我将分享一些关于网络流量监控的重要知识&#xff0c;并探讨如何在IT运维中运用这一工具优化网络性能&#xff0c;确保业务的顺畅进行。 1. 网…

Biological Psychiatry:内源性功能连接的特定模式与强迫症的伤害回避有关

摘要 强迫症(OCD)患者通常在没有实际威胁的情况下表现出持续的回避行为。强迫症对生活质量的影响和患者之间的异质性使得寻找新的大脑-行为干预目标十分有必要。基于啮齿类动物和非人灵长类动物持续回避行为的机制和解剖学研究&#xff0c;本研究的目标是测试持续回避行为相关…

用于脑肿瘤分割的跨模态深度特征学习| 文献速递-深度学习肿瘤自动分割

Title 题目 Cross-modality deep feature learning for brain tumor segmentation 用于脑肿瘤分割的跨模态深度特征学习 01 文献速递介绍 作为最致命的流行病&#xff0c;脑肿瘤的研究越来越受到关注。本文研究了一种基于深度学习的自动分割胶质瘤的方法&#xff0c;称为脑…

百度ERNIE系列预训练语言模型浅析(4)-总结篇

总结&#xff1a;ERNIE 3.0与ERNIE 2.0比较 &#xff08;1&#xff09;相同点&#xff1a; 采用连续学习 采用了多个语义层级的预训练任务 &#xff08;2&#xff09;不同点&#xff1a; ERNIE 3.0 Transformer-XL Encoder(自回归自编码), ERNIE 2.0 Transformer Encode…

Pandas-中axis的用法

在Pandas中&#xff0c;min(axis)方法是计算DataFrame或Series中每行或每列的最小值的函数。该函数可以接受一个参数axis&#xff0c;用于指定计算最小值的方向。当axis0时&#xff0c;表示沿着行的方向计算最小值&#xff1b;当axis1时&#xff0c;表示沿着列的方向计算最小值…

网络原理-------TCP协议

文章目录 TCP协议TCP协议段格式TCP原理确认应答机制 (安全机制)超时重传机制 (安全机制)连接管理机制 (安全机制)滑动窗口 (效率机制)流量控制 (安全机制)拥塞控制 (安全机制)延迟应答 (效率机制)捎带应答 (效率机制) 基于TCP的应用层协议 TCP协议 TCP, 即 Transmission Contr…

c语言 分而治之(施特拉森矩阵乘法)

给定两个大小分别为 nxn 的方阵 A 和 B&#xff0c;求它们的乘法矩阵。 朴素方法&#xff1a;以下是两个矩阵相乘的简单方法。 void multiply(int A[][N], int B[][N], int C[][N]) { for (int i 0; i < N; i) { for (int j 0; j < N; j) { …