原文链接:https://www.infoworld.com/article/3607372/how-to-work-with-record-types-in-csharp-9.html
原文标题:How to work with record types in C# 9
砂漠の果ての狼
利用 C# 9 中的record类型来构建不可变类型和线程安全对象。
不変性はオブジェクトをスレッドセーフにし、メモリ管理の改善に役立ちます。また、コードを読みやすく、メンテナンスしやすくします。不変オブジェクトとは、一度作成すると変更できないオブジェクトとして定義されます。したがって、不変オブジェクトは本質的にスレッドセーフであり、競合する条件の影響を受けません。
直到最近,C# 还不支持开箱即用的不可变性。C# 9 通过新的 init-only 属性和record类型引入了对不可变性的支持。仅init-only属性可用于使对象的各个属性不可变,而record可用于使整个对象不可变。
因为不可变对象不会改变它们的状态,所以在多线程和数据传输对象等许多用例中,不可变性是一个理想的特性。本文讨论了我们如何在 C# 9 中使用 init-only 属性和record类型。
要使用本文中提供的代码示例,您应该在系统中安装 Visual Studio 2019。如果您还没有安装,可以在此处下载 Visual Studio 2019。
Visual Studioでのコンソール·アプリケーション·プロジェクトの作成
まず、Visual Studioで. NET Coreコンソール·アプリケーション·プロジェクトを作成します。システムにVisual Studio 2019がインストールされていると仮定すると、以下の手順に従ってVisual Studioで新しい. NET Coreコンソールアプリケーションプロジェクトを作成します。
- Visual Studio IDEを起動します。
- “Create new project.”をクリックします。
- Create new projectウィンドウで、表示されるテンプレートのリストからConsole App. NET Coreを選択します。
- 次へクリック。
- 次に表示される“Configure your new project”ウィンドウで、新規プロジェクトの名前と場所を指定します。
- Creationをクリック。
これらの手順に従うと、Visual Studio 2019で新しい. NET Coreコンソールアプリケーションプロジェクトが作成されます。この項目は、この記事の後半で使用します。
C#9でのinit-onlyプロパティの使用
init-only属性是那些只能在对象初始化时赋值的属性。请参阅以下包含 init-only 属性的类。
public class DbMetadata
{
public string DbName { get; init; }
public string DbType { get; init; }
}
次のコードスニペットを使用して、DbMetadataクラスのインスタンスを作成し、そのプロパティを初期化できます。
DbMetadata dbMetadata = new DbMetadata()
{
DbName = "Test",
DbType = "Oracle"
};
请注意,对 init-only 字段的后续分配是非法的。因此,以下语句将无法编译。
dbMetadata.DbType = "SQL Server";
C#9でのレコード型の使用
C# 9 中的record类型是仅具有只读属性的轻量级、不可变数据类型(或轻量级类)。因为record类型是不可变的,所以它是线程安全的,并且在创建后不能改变或更改。您只能在构造函数中初始化record类型。
您可以使用 record 关键字声明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; }
}
请注意,仅将类型标记为record(如前面的代码片段所示)本身不会为您提供不可变性。要为您的record类型提供不可变性,您必须使用 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-only 属性会阻止这种情况。例如,以下代码片段将无法编译,因为默认情况下名为 Person 的record类型的所有属性都是 init-only。
var newPerson = person;
newPerson.Address = "112 Stafford Hills";
newPerson.City = "Bangalore";
幸运的是,有一个解决方法——with 关键字。通过指定属性值的更改,您可以利用 with 关键字从另一个record类型创建一个实例。以下代码片段说明了如何实现这一点。
var newPerson = person with
{ Address = "112 Stafford Hills", City = "Bangalore" };
C#9のレコード型継承
record类型支持继承。也就是说,您可以从现有record类型创建新record类型并添加新属性。以下代码片段说明了如何通过扩展现有record类型来创建新record类型。
public record Employee : Person
{
public int Id { get; init; }
public double Salary { get; init; }
}
C#9の位置レコード
默认情况下,使用位置参数创建的record类型实例是不可变的。换句话说,您可以通过使用构造函数参数传递有序的参数列表来创建record类型的不可变实例,如下面给出的代码片段所示。
var person = new Person("Joydip", "Kanjilal", "192/79 Stafford Hills", "Hyderabad", "India");
C#9でレコードインスタンスが等しいかどうかをチェックする
在 C# 中检查类的两个实例是否相等时,比较基于这些对象的引用(身份)。但是,如果您检查record类型的两个实例是否相等,则比较基于record类型的实例中的值。
以下代码片段说明了一个名为 DbMetadata 的record类型,它由两个字符串属性组成。
public record DbMetadata
{
public string DbName { get; init; }
public string DbType { get; init; }
}
次のコードスニペットは、DbMetadataレコードタイプの2つのインスタンスを作成する方法を示しています。
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 record类型的第三个实例的代码片段。请注意,实例 dbMetadata1 和 dbMetadata3 包含相同的值。
DbMetadata dbMetadata3 = new DbMetadata()
{
DbName = "Test",
DbType = "Oracle"
};
次の2つのステートメントは、コンソールウィンドウに“true”を表示します。
Console.WriteLine(dbMetadata1.Equals(dbMetadata3));
Console.WriteLine(dbMetadata3.Equals(dbMetadata1));
尽管record类型是引用类型,但 C# 9 提供了合成方法来遵循基于值的相等语义。编译器为您的record类型生成以下方法以强制实施基于值的语义:
Object.Equals(Object)方法的重载- 接受
record类型作为其参数的虚拟Equals方法 Object.GetHashCode()方法的重载- 两个相等运算符的方法,即
==运算符 和!=运算符 record类型实现System.IEquatable<T>
此外,记录类型提供了 Object.ToString() 方法的重载。这些方法是隐式生成的,您无需重新实现它们。
C#でのEqualsメソッドのチェック
您可以检查是否已隐式生成了 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);
}
コードをコンパイルすると、コンパイラーは以下のメッセージでエラーをマークします。
Type 'DbMetadata' already defines a member called 'Equals' with the same parameter types
尽管record类型是一个类,但 record 关键字提供了额外的类似值类型的行为和语义,使record与类不同。record本身是一种引用类型,但它使用自己的内置相等性检查——相等性是通过值而不是引用来检查的。最后,请注意record可以是可变的,但它们主要是为不变性而设计的。