如何在C#9中使用记录类型

利用C#9中的记录类型来构建不可变类型和线程安全对象。

不变性使您的对象具有线程安全性,并有助于改善内存管理。这也使您的代码更具可读性,更易于维护。不变的对象定义为一旦创建就无法更改的对象。因此,一个不变的对象本质上是线程安全的,并且不受竞争条件的影响。

直到最近,C#才开箱即用地支持不变性。C#9引入了对不可变性的支持,其中包含新的仅用于初始化的属性和记录类型。仅初始化属性可用于使对象的各个属性不可变,而记录可用于使整个对象不可变。

由于不可变对象不会更改其状态,因此在许多用例(例如多线程和数据传输对象)中,不可变性是理想的功能。本文讨论如何在C#9中使用仅初始化属性和记录类型

若要使用本文提供的代码示例,您应该在系统中安装Visual Studio 2019。如果您还没有副本,则可以在此处下载Visual Studio 2019如何在C#9中使用记录类型 

在Visual Studio中创建控制台应用程序项目

首先,让我们在Visual Studio中创建一个.NET Core控制台应用程序项目。假设系统中已安装Visual Studio 2019,请按照以下概述的步骤在Visual Studio中创建一个新的.NET Core控制台应用程序项目。

  1. 启动Visual Studio IDE。
  2. 点击“创建新项目”。
  3. 在“创建新项目”窗口中,从显示的模板列表中选择“控制台应用程序(.NET Core)”。
  4. 点击下一步。
  5. 在接下来显示的“配置新项目”窗口中,指定新项目的名称和位置。
  6. 单击创建。

完成这些步骤后,将在Visual Studio 2019中创建一个新的.NET Core控制台应用程序项目。我们将在本文的后续部分中使用该项目。

C#9中使用仅初始化属性

仅初始化属性是仅在对象初始化时才可以分配值的属性。请参阅包含仅初始化属性的以下类。

public class DbMetadata
    {
        public string DbName { get; init; }
        public string DbType { get; init; }
    }

您可以使用以下代码段创建DbMetadata类的实例并初始化其属性。

DbMetadata dbMetadata = new DbMetadata()
{
      DbName = "Test",
      DbType = "Oracle"
};      

请注意,随后对仅初始化字段的分配是非法的。因此,以下语句将无法编译。

dbMetadata.DbType = "SQL Server";

C#9中使用记录类型

C#9中的记录类型是仅具有只读属性的轻量级,不变数据类型(或轻量级类)。因为记录类型是不可变的,所以它是线程安全的,并且在创建记录后不能进行更改或更改。您只能在构造函数内部初始化记录类型

您可以使用record关键字声明一条记录,如下面的代码片段所示。

public record Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Address { get; set; }
  public string City { get; set; }
  public string Country { get; set; }
}      

请注意,仅将类型标记为记录(如前面的代码片段所示)不会单独给您带来不变性。要使记录类型具有不变性,您必须使用init属性,如下面的代码片段所示。

public record Person
{
  public string FirstName { get; init; }
  public string LastName { get; init; }
  public string Address { get; init; }
  public string City { get; init; }
  public string Country { get; init; }
}     

您可以使用以下代码片段创建Person类的实例并初始化其属性。

var person = new Person
{
  FirstName = "Joydip",
  LastName = "Kanjilal",
  Address = "192/79 Stafford Hills",
  City = "Hyderabad",
  Country = "India"
};

C#9中使用with表达式

如果某些属性具有相同的值,则您可能经常想从另一个对象创建一个对象。但是,记录类型的仅init属性将阻止这种情况。例如,以下代码段将不会编译,因为默认情况下,名为Person的记录类型的所有属性都是仅初始化的。

var newPerson = person;
newPerson.Address = "112 Stafford Hills";
newPerson.City = "Bangalore";

幸运的是,有一种解决方法-with关键字。通过指定属性值的更改,可以利用with关键字从另一个创建记录类型的实例。以下代码段说明了如何实现此目的。

var newPerson = person with
            { Address = "112 Stafford Hills", City = "Bangalore" };
                   

C#9记录类型的继承

记录类型支持继承。也就是说,您可以从现有记录类型创建新记录类型并添加新属性。以下代码段说明了如何通过扩展现有记录类型来创建新记录类型

public record Employee : Person
{
   public int Id { get; init; }
   public double Salary { get; init; }
}      

C#9中的位置记录

默认情况下,使用位置参数创建的记录类型的实例是不可变的。换句话说,可以通过使用构造函数自变量传递参数的有序列表来创建记录类型的不可变实例,如下面的代码段所示。

var person = new Person("Joydip", "Kanjilal", "192/79 Stafford Hills", "Hyderabad", "India");   

C#9中检查记录实例是否相等

在C#中检查类的两个实例是否相等时,比较基于这些对象的引用(身份)。但是,如果检查记录类型的两个实例是否相等,则比较将基于记录类型的实例中的值。

下面的代码片段说明了一个名为DbMetadata的记录类型,该记录类型由两个字符串属性组成。

public record DbMetadata
{
   public string DbName { get; init; }
   public string DbType { get; init; }
}       

以下代码段显示了如何创建DbMetadata记录类型的两个实例。

DbMetadata dbMetadata1 = new DbMetadata()
{
   DbName = "Test",
   DbType = "Oracle"
};
DbMetadata dbMetadata2 = new DbMetadata()
{
   DbName = "Test",
   DbType = "SQL Server"
};
     

您可以使用Equals方法检查是否相等。以下两个语句将在控制台窗口中显示“ false”。

Console.WriteLine(dbMetadata1.Equals(dbMetadata2));
Console.WriteLine(dbMetadata2.Equals(dbMetadata1));

请考虑以下代码片段,该代码片段创建DbMetadata记录类型的第三个实例。请注意,实例dbMetadata1和dbMetadata3包含相同的值。

DbMetadata dbMetadata3 = new DbMetadata()
{
   DbName = "Test",
   DbType = "Oracle"
}; 

以下两个语句将在控制台窗口中显示“ true”。

Console.WriteLine(dbMetadata1.Equals(dbMetadata3));
Console.WriteLine(dbMetadata3.Equals(dbMetadata1));

尽管记录类型是引用类型,但是C#9提供了遵循基于值的相等语义的综合方法。编译器为您的记录类型生成以下方法,以实施基于值的语义:

  • Object.Equals(Object)方法的重写
  • 接受记录类型作为其参数的虚拟Equals方法
  • Object.GetHashCode()方法的重写
  • 两个相等运算符的方法,即operator ==和operator!=
  • 记录类型实现System.IEquatable <T>

此外,记录类型提供Object.ToString()方法的替代。这些方法是隐式生成的,您无需重新实现它们。

在C#中检查等于方法

您可以检查Equals方法是否已隐式生成。为此,请在DbMetadata记录中添加一个Equals方法,如下所示。

 public record DbMetadata
    {
        public string DbName { get; init; }
        public string DbType { get; init; }
        public override bool Equals(object obj) =>
        obj is DbMetadata dbMetadata && Equals(dbMetadata);
    }

编译代码时,编译器将使用以下消息标记错误:

类型“ DbMetadata”已经定义了具有相同参数类型的名为“等于”的成员

尽管记录类型是类,但record关键字提供了其他类似于值的行为和语义,从而使记录与类有所不同。记录本身是引用类型,但是它使用自己的内置相等性检查-相等性是通过值而不是引用来检查的。最后,请注意记录可以是可变的,但它们主要是为不变性而设计的。

原创文章,作者:冰封一夏,如若转载,请注明出处:http://www.nncjzx.com/1370.html

关注本站公众号获取更多实时内容

本站微信公众号:二线码农