当前位置: 首页 > news >正文

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的设计初衷

综上所述:

  1. record实质是微软提供的一个语法糖,本质就是一个类,这个类里生成了若干个方法,这些方法就使得record与类区别开来
  2. record的写法简单快捷,可用在数据传输对象中,如dto等,能够大大提升效率,简化代码

相关文章:

  • echarts词云图echarts-wordcloud使用方法
  • HarmonyOS--ArkTS(1)--基本语法(1)
  • c++新经典模板与泛型编程:const修饰符的移除与增加
  • Python函数和模块:编程的魔法函数
  • Leetcode刷题笔记——摩尔投票法
  • C++初阶(十四)list
  • [论文阅读]BEVFusion
  • Spring Boot 优雅地处理重复请求
  • Java IO流(五)(字符集基础知识简介)
  • 【3】密评-物理和环境安全测评
  • 分布式分布式事务分布式锁分布式ID
  • 1688API接口系列,商品详情数据丨搜索商品列表丨商家订单类丨1688开放平台接口使用方案
  • SQL语言重温
  • 安全快速地删除 MySQL 大表数据并释放空间
  • 忘记PDF密码了,怎么办?
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • Apache Zeppelin在Apache Trafodion上的可视化
  • C++入门教程(10):for 语句
  • create-react-app项目添加less配置
  • crontab执行失败的多种原因
  • css布局,左右固定中间自适应实现
  • docker-consul
  • hadoop集群管理系统搭建规划说明
  • happypack两次报错的问题
  • JavaScript DOM 10 - 滚动
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • JS+CSS实现数字滚动
  • JS笔记四:作用域、变量(函数)提升
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • maven工程打包jar以及java jar命令的classpath使用
  • PAT A1017 优先队列
  • python大佬养成计划----difflib模块
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Wamp集成环境 添加PHP的新版本
  • 动态魔术使用DBMS_SQL
  • 工程优化暨babel升级小记
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 怎么把视频里的音乐提取出来
  • gunicorn工作原理
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • #QT(智能家居界面-界面切换)
  • #大学#套接字
  • (13):Silverlight 2 数据与通信之WebRequest
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (一)u-boot-nand.bin的下载
  • (正则)提取页面里的img标签
  • (转)linux 命令大全
  • (转)winform之ListView