架构师_程序员_码农网

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 2653|回复: 0

[资料] .NET/C# 新特性之 record 记录类型

[复制链接]
发表于 2023-10-2 09:25:51 | 显示全部楼层 |阅读模式
从 C# 9 开始,可以使用 record 修饰符定义一个引用类型,用来提供用于封装数据的内置功能。 C# 10 允许 record class 语法作为同义词来阐明引用类型,并允许 record struct 使用相同功能定义值类型。

C# 中的 record 的应用场景(ChatGPT 回答):

C# 中的 record 类型是在最新版本中引入的一种新特性。它可以用于许多场景,包括但不限于以下几个方面:

不可变数据对象:record 类型是不可变的,这意味着一旦创建,其属性值就不能被修改。这使得它们非常适合表示不可变的数据结构,如传输对象(DTO)或配置设置。
值对象:record 类型可以用于表示值对象,这些对象的相等性是基于其属性值而不是引用相等性来确定的。这使得它们非常适合用于比较和存储值对象,如日期范围、坐标点等。
模式匹配:record 类型可以与模式匹配一起使用,这使得在处理复杂的数据结构时更加方便。您可以使用模式匹配来检查和提取 record 对象的属性值,以便执行相应的操作。
不可变集合:record 类型可以与不可变集合类型(如 ImmutableList、ImmutableDictionary 等)一起使用,以创建不可变的集合对象。这样可以确保集合的内容不会被修改,从而提高代码的可靠性和安全性。
这些只是 record 类型的一些使用场景,您可以根据具体的需求和代码结构来决定是否使用 record 类型。
record 与 class 和 struct 的区别

声明和实例化类或结构时使用的语法与操作记录时的相同。 只是将 class 关键字替换为 record,或者使用 record struct 而不是 struct。 同样地,记录类支持相同的表示继承关系的语法。 记录与类的区别如下所示:

  • 可在主构造函数中使用位置参数来创建和实例化具有不可变属性的类型。
  • 在类中指示引用相等性或不相等的方法和运算符(例如 Object.Equals(Object) 和 ==)在记录中指示值相等性或不相等。
  • 可使用 with 表达式对不可变对象创建在所选属性中具有新值的副本。
  • 记录的 ToString 方法会创建一个格式字符串,它显示对象的类型名称及其所有公共属性的名称和值。
  • 记录可从另一个记录继承。 但记录不可从类继承,类也不可从记录继承。
  • 记录结构与结构的不同之处是,编译器合成了方法来确定相等性和 ToString。 编译器为位置记录结构合成 Deconstruct 方法。


编译器为 record class 中的每个主构造函数参数合成一个公共仅初始化属性。 在 record struct 中,编译器合成公共读写属性。 编译器不会在不包含 record 修饰符的 class 和 struct 类型中创建主构造函数参数的属性。

record 两种类型

record class 引用类型(默认:class 可以省略)
record struct 值类型

record 语法糖

record 其实就是语法糖,最终生成的还是 class 或 struct 代码。如下代码为例:

最终编译成如下代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.DisableOptimizations)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: System.Runtime.CompilerServices.RefSafetyRules(11)]

[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public class PersonInfo : IEquatable<PersonInfo>
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string <FirstName>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string <LastName>k__BackingField;

    [CompilerGenerated]
    protected virtual Type EqualityContract
    {
        [CompilerGenerated]
        get
        {
            return typeof(PersonInfo);
        }
    }

    public string FirstName
    {
        [CompilerGenerated]
        get
        {
            return <FirstName>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <FirstName>k__BackingField = value;
        }
    }

    public string LastName
    {
        [CompilerGenerated]
        get
        {
            return <LastName>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <LastName>k__BackingField = value;
        }
    }

    public PersonInfo(string FirstName, string LastName)
    {
        <FirstName>k__BackingField = FirstName;
        <LastName>k__BackingField = LastName;
        base..ctor();
    }

    [CompilerGenerated]
    public override string ToString()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("PersonInfo");
        stringBuilder.Append(" { ");
        if (PrintMembers(stringBuilder))
        {
            stringBuilder.Append(' ');
        }
        stringBuilder.Append('}');
        return stringBuilder.ToString();
    }

    [CompilerGenerated]
    protected virtual bool PrintMembers(StringBuilder builder)
    {
        RuntimeHelpers.EnsureSufficientExecutionStack();
        builder.Append("FirstName = ");
        builder.Append((object)FirstName);
        builder.Append(", LastName = ");
        builder.Append((object)LastName);
        return true;
    }

    [System.Runtime.CompilerServices.NullableContext(2)]
    [CompilerGenerated]
    public static bool operator !=(PersonInfo left, PersonInfo right)
    {
        return !(left == right);
    }

    [System.Runtime.CompilerServices.NullableContext(2)]
    [CompilerGenerated]
    public static bool operator ==(PersonInfo left, PersonInfo right)
    {
        return (object)left == right || ((object)left != null && left.Equals(right));
    }

    [CompilerGenerated]
    public override int GetHashCode()
    {
        return (EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(<FirstName>k__BackingField)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(<LastName>k__BackingField);
    }

    [System.Runtime.CompilerServices.NullableContext(2)]
    [CompilerGenerated]
    public override bool Equals(object obj)
    {
        return Equals(obj as PersonInfo);
    }

    [System.Runtime.CompilerServices.NullableContext(2)]
    [CompilerGenerated]
    public virtual bool Equals(PersonInfo other)
    {
        return (object)this == other || ((object)other != null && EqualityContract == other.EqualityContract && EqualityComparer<string>.Default.Equals(<FirstName>k__BackingField, other.<FirstName>k__BackingField) && EqualityComparer<string>.Default.Equals(<LastName>k__BackingField, other.<LastName>k__BackingField));
    }

    [CompilerGenerated]
    public virtual PersonInfo <Clone>$()
    {
        return new PersonInfo(this);
    }

    [CompilerGenerated]
    protected PersonInfo(PersonInfo original)
    {
        <FirstName>k__BackingField = original.<FirstName>k__BackingField;
        <LastName>k__BackingField = original.<LastName>k__BackingField;
    }

    [CompilerGenerated]
    public void Deconstruct(out string FirstName, out string LastName)
    {
        FirstName = this.FirstName;
        LastName = this.LastName;
    }
}

namespace Microsoft.CodeAnalysis
{
    [CompilerGenerated]
    [Embedded]
    internal sealed class EmbeddedAttribute : Attribute
    {
    }
}

namespace System.Runtime.CompilerServices
{
    [CompilerGenerated]
    [Microsoft.CodeAnalysis.Embedded]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
    internal sealed class NullableAttribute : Attribute
    {
        public readonly byte[] NullableFlags;

        public NullableAttribute(byte P_0)
        {
            byte[] array = new byte[1];
            array[0] = P_0;
            NullableFlags = array;
        }

        public NullableAttribute(byte[] P_0)
        {
            NullableFlags = P_0;
        }
    }

    [CompilerGenerated]
    [Microsoft.CodeAnalysis.Embedded]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
    internal sealed class NullableContextAttribute : Attribute
    {
        public readonly byte Flag;

        public NullableContextAttribute(byte P_0)
        {
            Flag = P_0;
        }
    }

    [CompilerGenerated]
    [Microsoft.CodeAnalysis.Embedded]
    [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
    internal sealed class RefSafetyRulesAttribute : Attribute
    {
        public readonly int Version;

        public RefSafetyRulesAttribute(int P_0)
        {
            Version = P_0;
        }
    }
}
它会自动生成构造函数,并且重写了 ToString、GetHashCode、Equals 方法,并且自动生成了一些方法。


p1 和 p2 实际上是两个不同的对象,但是由于编译器帮我们自动生成了比较器(IEquatable),重写了 Equals、GetHashCode 方法,导致输出为 true。调用 ToString 方法也非常直观的输出记录的值。如下图:

.NET/C# 实现 IEqualityComparer 自定义比较器
https://www.itsvse.com/thread-10643-1-1.html

QQ截图20231002090444.jpg

record 中的 required、init、with 语法

required:required 修饰符表示其所应用的字段或属性必须由所有构造函数或使用对象初始值设定项进行初始化。 用于初始化该类型新实例的任何表达式都必须初始化所有必需的成员。
参考:超链接登录可见。

init:在 C# 9 及更高版本中,init 关键字在属性或索引器中定义访问器方法。 init-only 资源库仅在对象构造期间为属性或索引器元素赋值。 这会强制实施不可变性,因此,一旦初始化对象,将无法再更改。
参考:超链接登录可见。

with:如果需要复制包含一些修改的实例,可以使用 with 表达式来实现非破坏性变化。 with 表达式创建一个新的记录实例,该实例是现有记录实例的一个副本,修改了指定属性和字段。
参考:超链接登录可见。

所以 record 可以定义如下:

required、init 就不说了,with 其实也是语法糖,它会调用自动生成的 <Clone>$ 方法,然后再进行修改属性的值。如下:

QQ截图20231002091826.jpg

代码:

QQ截图20231002092006.jpg

record 属性添加特性

有时候我们需要在属性上面添加一些特性,例如:json 序列化的特性,描述信息等。

QQ截图20231002092421.jpg

参考资料:

超链接登录可见。
超链接登录可见。





上一篇:ASP.NET Core(二十四)基于 Refit、MemoryPack 高性能通信
下一篇:GitHub 通过 REST API 查看仓库大小
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:help@itsvse.com

QQ|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2025-6-15 20:35

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表