• C#9.0记录类型


    记录类型

        引用类型

        提供合成方法来提供值语义,从而实现相等性。默认情况下,记录是不可变的。

        记录类型可以轻松创建不可变的引用类型。以前.net类型主要分为引用类型(包括类和匿名类型)和值类型(包括结构和元组)。虽然建议使用不可变的值类型,但可变的值类型通常不会引入错误。值类型变量可保存值,因此在将值类型传递给方法时,会对原始数据的副本进行更改。

    不可变的引用类型。记录为不可变的引用类型提供类型声明,该引用类型使用值语义实现相等性。

    如果用于实现相等性的合成方法的属性和哈希代码的属性都相等,则认为两条记录相等。

    请考虑一下定义:

    public record Person{    public string LastName{get;}    public string FirstName{get;}    public Person(string first,string last)=>(FirstName,LastName)=(first,last);}

    记录定义会创建一个包含两个只读属性(FirstName和LastName)的Person类型。Person类型是引用类型。如果查看IL,它就是一个类。它是不可变的,因为在创建它后,无法修改任何属性。定义记录类型时,编译器会合成其他几种方法:

        基于值的相等性比较方法

        替代GetHashCode()

        复制和克隆成员

        PrintMembers和ToString()

    记录支持继承。可声明派生自Person的新记录,如下所示:​​​​​​​

    public record Teacher:Person{    public string Subject{get;}    public Teacher(string first,string last,string sub):    base(first,last)=>Subject=sub;}

    还可以密封记录防止进一步派生:​​​​​​​

    public sealed record Student:Person{    public int Level{get;}    public Student(string first,string last,int level):    base(first,last)=>Level=level;}

    编译器会合成上述方法的不同版本。方法签名取决于记录类型是否密封以及直接基类是否为对象。记录应具有以下功能:

        相等性是基于值的,包括检查类型是否匹配。例如,即使两条记录名称相同,Student与不能等于Person。

        记录具有为你生成的一致的字符串表示形式。

        记录支持副本构造。正确的副本构造必须包括继承层次结构和开发人员添加属性。

        可通过修改复制记录。这些复制和修改操作支持非破坏性转变。

    除了熟悉的Equals重载、operator == 和operator !=外,编译器还会合成新的EqualityContract属性。该属性返回与记录类型匹配的Type对象。如果基类型为object,则属性为virtual。如果基类型是其他记录类型,则属性为override。如果记录类型为sealed,则属性为sealed。合成的GetHashCode使用基类型和记录类型中声明的所有属性和字段中GetHashCode。这些合成方法在整个继承层次结构中强制执行基于值的相等性。这意味着,绝不会将Student视为同名的Person相等。两条几率的类型必须匹配,而且几率类型之间共享的所有属性也必须相等。

    记录还具有合成的构造函数和用于创建副本的“克隆”方法。合成的构造函数有一个记录类型的参数。该函数会为记录的所有属性生成具有相同值的新纪录。如果记录是密封的,则此构造函数是专用函数;否则它将受到保护。合成的“克隆”方法支持用于记录层次结构的副本构造。“克隆”一词用引号引起来没因为实际名称是编译器生成的。无法在记录类型中创建名为Clone的方法。合成的“克隆”方法返回使用虚拟调度复制的记录类型。编译器根据record上的访问修饰符为“克隆”方法添加不同的修饰符:

        如果记录类型为abstract,则“克隆”方法也为abstract。如果基类型不是object,则方法也是override。

        当基类型为object时,对于不是abstract的记录类型:

            如果记录为sealed,则“克隆”方法添加其他修饰符(这意味着它不是virtual)。

            如果记录不是sealed,则“克隆”方法为virtual。

        当基类型不是object时,对于不是abstract的记录类型:

            如果记录是sealed,则“克隆”也是sealed。

            如果记录不是sealed,则“克隆”方法为override。

    所有这些规则的结果都是,跨记录类型的任何层次结构一致的实现了相等性。如果亮条记录的属性相等且类型相同,则它们彼此相等,如下所示:

        var person=new Person("Bill","Wagner");

        var student=new Student("Bill","Wagner",11);

        Console.WriteLine(student==person); //false

    编译器合成了两种支持打印输出的方法:ToString()替代和PrintMembers。PrintMembers采用System.Text.StringBuilder作为其参数。他对记录类型中的所有属性追加一个用逗号分隔的属性名称和值的列表。PrintMembers会调用派生自其它记录的任何记录的基本实现。ToString()替代会返回由PrintMembers生成的字符串,并将其括在"{"和"}"内。例如,Student的ToString()方法返回一个string,类似于以下代码:

        "Student { LastName = Wagner,FirstName = Bill,Level=11}"

    截止目前显示的示例都使用传统语法声明属性。还有一种更简洁的格式,称为“位置记录”。下面是先前定义为位置记录的3种记录类型:

        public record Person(string FirstName,string LastName);

        public record Teacher(string FirstName,string LastName.string Subject):Person(FirstName,LastName);

        public sealed record Studemt(string FirstName,string LastName,int Level):Person(FirstName,LastName);

    这些声明创建的新功能与早期版本相同(以下部分剑圣了几项额外的功能)。这些声明以分好而不是方括号结尾,因为这些记录没有添加其他方法。可添加正文,还可包括其他任何方法:

        publicrecordPet(stringName)    {        publicvoidShredTheFurniture() =>Console.WriteLine("Shredding furniture");    }    publicrecordDog(stringName) :Pet(Name)    {        publicvoidWagTail() =>Console.WriteLine("It's tail wagging time");        publicoverridestringToString()            {            StringBuilder s = new();base.PrintMembers(s);            return $"{s.ToString()}is a dog";    }}

    编译器 为位置记录生成Deconstruct方法。Deconstruct方法的参数与记录类型中所有公共属性的名称匹配。Deconstruct方法可用于讲记录析构为其组件属性:​​​​​​​

        var person=new Person("Bill","Wagner");    var (first,last)=person;    Console.WriteLine(first);    Constle.WriteLine(last);

    最后,记录支持with表达式。with表达式指示编译器创建记录的副本,但修改制定的属性*:

        Person brother=person with{ FirstName = "Paul" };

    上一行创建新的Person记录,其中LastName属性是person的副本,FirstName为"Paul"。可在with表达式中设置任意数量的属性。还可以使用with表达式来创建精确的副本。为要修改的属性指定空集:

        Person clone=person with {};

    你可编写除“克隆”方法以外的任何合成成员。 如果记录类型的方法与任何合成方法的签名匹配,则编译器不会合成该方法。

  • 相关阅读:
    Python中计算程序的运行时间——timeit模块
    基础 | NIO - [IO 发展]
    Go单元测试及框架使用
    CV基础常用知识点
    测试面试 | 某 BAT 大厂测试开发面试真题与重点解析
    虚拟机Ubuntu18.04系统使用时所需要的便利配置选项
    一个.Net开发的功能强大、易于使用的流媒体服务器和管理系统
    Flutter-无限循环滚动标签
    Springboot面向个性化推荐的教务管理系统720em计算机毕业设计-课程设计-期末作业-毕设程序代做
    【数据结构】内部排序小结- -归并排序
  • 原文地址:https://blog.csdn.net/biyusr/article/details/125498932