c#编码技巧(十五):新语法糖record深入分析
c#编码技巧(十四):新语法糖record深入分析
从 C# 9 开始新增了一个关键字record,用于封装数据。
record实质是微软提供的一个语法糖,因很多开源项目都用到了这个关键字,说明这个语法糖比较实用。
那么这个record类型和普通class类型有什么区别呢?我们可以通过工具探究一下源码
首先新建一个普通的类PersonCls,添加两个属性
/// <summary>/// 普通类/// </summary>public class PersonCls{public string Name { get; set; }public string Address { get; set; }}
再建一个record类型PersonRecord,record类型的声明简洁:一行就搞定,其中属性名字放在括号里,编译器自动为我们生成两个属性
/// <summary>/// record类型/// </summary>/// <param name="Name"></param>/// <param name="Age"></param>public record PersonRecord(string Name, int Age);//也即public record class PersonRecord(string Name, int Age);//其中class可省略var person = new PersonRecord("Tom", 18);
利用反编译工具查看普通类PersonCls代码,果然非常普通,除了两属性什么都没有
using System;
using System.Runtime.CompilerServices;namespace DotNet6
{// Token: 0x02000005 RID: 5[NullableContext(1)][Nullable(0)]public class PersonCls{// Token: 0x17000001 RID: 1// (get) Token: 0x06000005 RID: 5 RVA: 0x00002092 File Offset: 0x00000292// (set) Token: 0x06000006 RID: 6 RVA: 0x0000209A File Offset: 0x0000029Apublic string Name { get; set; }// Token: 0x17000002 RID: 2// (get) Token: 0x06000007 RID: 7 RVA: 0x000020A3 File Offset: 0x000002A3// (set) Token: 0x06000008 RID: 8 RVA: 0x000020AB File Offset: 0x000002ABpublic string Address { get; set; }}
}
同样利用反编译工具查看PersonRecord,却生成了很多代码
其中
- 包含一个有参构造函数
- 生成了两个属性
- 生成了ToString和运算符比较、Equals比较的方法
- 还有其他一些方法,方法都很简单暂不做分析
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;namespace DotNet6
{// Token: 0x02000006 RID: 6[NullableContext(1)][Nullable(0)]public class PersonRecord : IEquatable<PersonRecord>{// Token: 0x0600000A RID: 10 RVA: 0x000020BD File Offset: 0x000002BDpublic PersonRecord(string Name, string Address){this.Name = Name;this.Address = Address;base..ctor();}// Token: 0x17000003 RID: 3// (get) Token: 0x0600000B RID: 11 RVA: 0x000020D4 File Offset: 0x000002D4[CompilerGenerated]protected virtual Type EqualityContract{[CompilerGenerated]get{return typeof(PersonRecord);}}// Token: 0x17000004 RID: 4// (get) Token: 0x0600000C RID: 12 RVA: 0x000020E0 File Offset: 0x000002E0// (set) Token: 0x0600000D RID: 13 RVA: 0x000020E8 File Offset: 0x000002E8public string Name { get; set; }// Token: 0x17000005 RID: 5// (get) Token: 0x0600000E RID: 14 RVA: 0x000020F1 File Offset: 0x000002F1// (set) Token: 0x0600000F RID: 15 RVA: 0x000020F9 File Offset: 0x000002F9public string Address { get; set; }// Token: 0x06000010 RID: 16 RVA: 0x00002104 File Offset: 0x00000304[CompilerGenerated]public override string ToString(){StringBuilder stringBuilder = new StringBuilder();stringBuilder.Append("PersonRecord");stringBuilder.Append(" { ");if (this.PrintMembers(stringBuilder)){stringBuilder.Append(' ');}stringBuilder.Append('}');return stringBuilder.ToString();}// Token: 0x06000011 RID: 17 RVA: 0x00002150 File Offset: 0x00000350[CompilerGenerated]protected virtual bool PrintMembers(StringBuilder builder){RuntimeHelpers.EnsureSufficientExecutionStack();builder.Append("Name = ");builder.Append(this.Name);builder.Append(", Address = ");builder.Append(this.Address);return true;}// Token: 0x06000012 RID: 18 RVA: 0x0000218A File Offset: 0x0000038A[NullableContext(2)][CompilerGenerated]public static bool operator !=(PersonRecord left, PersonRecord right){return !(left == right);}// Token: 0x06000013 RID: 19 RVA: 0x00002196 File Offset: 0x00000396[NullableContext(2)][CompilerGenerated]public static bool operator ==(PersonRecord left, PersonRecord right){return left == right || (left != null && left.Equals(right));}// Token: 0x06000014 RID: 20 RVA: 0x000021AC File Offset: 0x000003AC[CompilerGenerated]public override int GetHashCode(){return (EqualityComparer<Type>.Default.GetHashCode(this.EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.<Name>k__BackingField)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.<Address>k__BackingField);}// Token: 0x06000015 RID: 21 RVA: 0x000021EC File Offset: 0x000003EC[NullableContext(2)][CompilerGenerated]public override bool Equals(object obj){return this.Equals(obj as PersonRecord);}// Token: 0x06000016 RID: 22 RVA: 0x000021FC File Offset: 0x000003FC[NullableContext(2)][CompilerGenerated]public virtual bool Equals(PersonRecord other){return this == other || (other != null && this.EqualityContract == other.EqualityContract && EqualityComparer<string>.Default.Equals(this.<Name>k__BackingField, other.<Name>k__BackingField) && EqualityComparer<string>.Default.Equals(this.<Address>k__BackingField, other.<Address>k__BackingField));}// Token: 0x06000018 RID: 24 RVA: 0x0000225F File Offset: 0x0000045F[CompilerGenerated]protected PersonRecord(PersonRecord original){this.Name = original.<Name>k__BackingField;this.Address = original.<Address>k__BackingField;}// Token: 0x06000019 RID: 25 RVA: 0x00002280 File Offset: 0x00000480[CompilerGenerated]public void Deconstruct(out string Name, out string Address){Name = this.Name;Address = this.Address;}}
}
使用这些方法,可以看到与普通class类不同的是:
- record的ToString()可以输出值
- 属性值相同的两个record类型,使用==或Equals比较,判断为相等
static async Task Main(string[] args){//record类型ToString()可以输出名称+值,而类只会输出命名空间+类名var clsStr = (new PersonCls() { Name = "LiLei", Address="GD" }.ToString());var str = (new PersonRecord("LiLei", "GD")).ToString();//输出:PersonRecord { Name = LiLei, Address = GD }//属性值相同的普通类,使用==或Equals比较,判断为不相等var cls1 = new PersonCls() { Name = "Tom", Address = "CN" };var cls2 = new PersonCls() { Name = "Tom", Address = "CN" };bool isClassOperatorEquals = cls1 == cls2;//falsebool isClassEquals = cls1.Equals(cls2);//falsebool isClassReferenceEquls = ReferenceEquals(cls1, cls2);//false//属性值相同的两个record类型,使用==或Equals比较,判断为相等;使用ReferenceEquals比较,判断为不相等,因为引用是确实是不同var rcd1 = new PersonRecord("Ben", "HK");var rcd2 = new PersonRecord("Ben", "HK");bool isRecordOperatorEquals = rcd1 == rcd2;// true:因为值相同bool isRecordEquals = rcd1.Equals(rcd2);//true:因为值相同bool isRecordReferenceEquls = ReferenceEquals(rcd1, rcd2);//false:引用不同//rcd1.Address = "shenzhen";//定义在括号内的record的属性值,在new之后不能修改;//想要能修改的属性,就像Level那样声明/*public record PersonRecordCanChange(string Name, string Address){public string Level { get; set; }}*/}
record还有其他用法,比如使用with { }可以复制这个record
var rcdCopy = rcd1 with { };//复制Console.WriteLine($"name={rcdCopy.Name}, address={rcdCopy.Address}");//name=Ben, address=HK
只想部分复制,可以在{}内更改部分属性的值
var rcdCopy = rcd1 with { };//复制Console.WriteLine($"name={rcdCopy.Name}, address={rcdCopy.Address}");//name=Ben, address=Beijing
可以继承
public record Animal(string Head, string Body);public record Human(string Head, string Body, string Hand): Animal(Head, Body);
如果确实需要自定义属性,可以这样写
public record Points{//声明属性X,Y,并在构造函数中注入赋值public Points(double time, double distance) => (X, Y) = (time, distance);public double X { get; set; }public double Y { get; set; }}var point = new Points(time: 10, distance: 990);var x = point.X;var y = point.Y;
但不建议这样做,这样违背了record的设计初衷
综上所述:
- record实质是微软提供的一个语法糖,本质就是一个类,这个类里生成了若干个方法,这些方法就使得record与类区别开来
- record的写法简单快捷,可用在数据传输对象中,如dto等,能够大大提升效率,简化代码
相关文章:
c#编码技巧(十五):新语法糖record深入分析
c#编码技巧(十四):新语法糖record深入分析 从 C# 9 开始新增了一个关键字record,用于封装数据。 record实质是微软提供的一个语法糖,因很多开源项目都用到了这个关键字,说明这个语法糖比较实用。 那么这个record类型和普通class类…...
Java IO流(五)(字符集基础知识简介)
字符集 计算机的存储规则(英文字符) 常见字符集介绍 a.GB2312字符集:1980年发布,1981年5月1日实施的简体中文汉字编码国家标准。收录7445个图形字符,其中包括6763个简体汉字 b.BIG5字符集:台湾地区繁体中…...
周周爱学习之Redis重点总结
redis重点总结 在正常的业务流程中,用户发送请求,然后到缓存中查询数据。如果缓存中不存在数据的话,就会去数据库查询数据。数据库中有的话,就会更新缓存然后返回数据,数据库中也没有的话就会给用户返回一个空。 1.缓…...
免费的SEO外链发布工具,提升排名的利器
互联网已经成为信息传播和商业发展的重要平台。而对于拥有网站的个人、企业来说,如何让自己的网站在搜索引擎中脱颖而出?SEO(Search Engine Optimization)作为提高网站在搜索引擎中排名的关键手段. 什么是SEO外链? S…...
腾讯字节常考的linux命令
1 ps 1.1 ps -ef 有哪些字段 ps -ef 命令在Unix/Linux系统中用于显示当前运行的进程。输出的字段通常包括: UID:启动进程的用户ID。PID:进程ID。PPID:父进程ID。C:CPU利用率。STIME:进程启动时间。TTY&a…...
JAVA后端自学技能实操合集
JAVA后端自学技能实操 内容将会持续更新中,有需要添加什么内容可以再评论区留言,大家一起学习FastDFS使用docker安装FastDFS(linux)集成到springboot项目中 内容将会持续更新中,有需要添加什么内容可以再评论区留言,大家一起学习 FastDFS 组名:文件上传后所在的 st…...
C++ 关联容器
关联容器 关联容器支持高效的关键字查找和访问。 两个主要的关联容器(associative container)类型是 map 和 set。 map 中的元素是一些关键字——值对。 关键字起到索引的作用,值则表示与索引相关联的数据。 set 中的每个元素只包含一个关键…...
ES6之函数新增的扩展
参数 ES6允许为函数的参数设置默认值 function log(x, y World) {console.log(x, y); }console.log(Hello) // Hello World console.log(Hello, China) // Hello China console.log(Hello, ) // Hello函数的形参是默认声明的,不能使用let或const再次声明 functi…...
postgresql安装部署(docker版本)
1.在线部署 创建数据库存储目录 mkdir /home/pgdata创建容器 docker run --name postgresql --restartalways -d -p 5432:5432 -v /home/pgdata:/var/lib/postgresql/data --shm-size10g -e POSTGRES_PASSWORD密码 postgis/postgis:12-3.2-alpine–name为设置容器名称 -d表…...
【Python/Java/C++三种语言】20天拿下华为OD笔试之【位运算】2023B-出错的或电路【欧弟算法】全网注释最详细分类最全的华为OD真题
文章目录 题目描述与示例题目描述输入描述输出描述示例一输入输出说明 示例二输入输出说明 解题思路代码PythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目描述与示例 题目描述 某生产门电路的厂商发现某一批次的或门电路不稳定,具体现象为计…...
vscode 编译运行c++ 记录
一、打开文件夹,新建或打开一个cpp文件 二、ctrl shift p 进入 c/c配置 进行 IntelliSense 配置。主要是选择编译器、 c标准, 设置头文件路径等,配置好后会生成 c_cpp_properties.json; 二、编译运行: 1、选中ma…...
错题总结(四)
1.【一维数组】输入10个整数,求平均值 编写一个程序,从用户输入中读取10个整数并存储在一个数组中。然后,计算并输出这些整数的平均值。 int main() {int arr[10];int sum 0;for (int n 0; n < 10; n){scanf("%d", &arr…...
ORACLE使用Mybatis-plus批量插入
ORACLE使用mybatis-plus自带的iservice.saveBatch方法时,会报DML Returing cannot be batch错误: 推测原因是oracle不支持insert into table_name (,) values (,),()的写法。且oracle不会自动生…...
vue,uniapp的pdf等文件在线预览
vue,uniapp文件在线预览方案,用了个稍微偏门一点的方法实现了 通过后端生成文件查看页面,然后前端只要展示这个网页就行,uniapp就用web-view来展示,后台系统就直接window.open()打开就行 示例查看PDF文件,…...
SpringBoot 项目 Jar 包加密,防止反编译
1场景 最近项目要求部署到其他公司的服务器上,但是又不想将源码泄露出去。要求对正式环境的启动包进行安全性处理,防止客户直接通过反编译工具将代码反编译出来。 2方案 第一种方案使用代码混淆 采用proguard-maven-plugin插件 在单模块中此方案还算简…...
DockerFile中途执行出错的解决办法
DockerFile中途执行出错的解决办法 你们是否也曾经因为DockerFile中途执行出错,而对其束手无策?总是对docker避之不及! 但是当下载的源码运用到了docker,dockerFile 执行到一半,报错了怎么办? 现状 那么当DockerFile执行一半出错后,会产生什么结果呢? 如图可知,生成…...
Word插件-好用的插件-一键设置字体--大珩助手
常用字体 整理了论文、公文常用字体 整理了常用的论文字体,可一键设置当前节或选择的文字的字体 字体设置 包含字体选择、字体颜色 特殊格式 包含首字下沉、段落分栏、统一宽度、双行合一、上标切换、下标切换、转为全角、转为半角、挖词填空、当前日期、大写金…...
【MODBUS】Modbus主站云端服务器和边缘设备部署区别
Modbus主站作为云端服务器: 云端服务器作为主站: 在这种部署方式中,云端服务器充当Modbus通信的主站,负责向不同的Modbus从站发起请求,并处理响应。云端服务器通常与其他云服务一起运行,可以在云平台上实现…...
hbuiler中使用npm安装datav
注:datav边框样式目前使用时:适用于网页,不适用于app 1、先安装node 安装、配置Node路径 2、为Node配置环境变量 3、在hbuilder的设置中填写node的路径 配置 4、打开cmd输入npm install jiaminghi/data-view 安装dataV,&…...
贾佳亚团队新作LLaMA-VID,2token让大模型学会看好莱坞大片
家人们谁懂,连大模型都学会看好莱坞大片了,播放过亿的GTA6预告片大模型还看得津津有味,实在太卷了! 而让LLM卷出新境界的办法简单到只有2token——将每一帧编码成2个词即可搞定。等等!这种大道至简的方法有种莫名的熟…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
