個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Xamarin に関する事を中心に書いています。 Microsoft MVP for Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

C# 10 の新機能 - レコード構造体

C# 10 の新機能の情報の目次は以下をご覧ください。
hiro128.hatenablog.jp 
 

レコード構造体

C# 9 でレコードクラスが追加されましたが、C# 10 ではレコード構造体が追加されました。
C# 10 ではレコード構造体が追加されたことで、クラスより速度が出る構造体でレコード型を利用可能になりました。
 

導入の意図

レコード型(レコード構造体、レコードクラス)は、簡潔なコードで便利にデータを格納するための型です。

通常のクラスや構造体でデータモデルを作成しようとすると、データとしての等価性をサポートするための Equals のオーバーライドや operator == のオーバーロード、データ表示用の書式設定のための ToString のオーバーライドやデータを不変にするための構文など面倒な手間が生じます。

レコード型を利用すると、不変なプロパティを持つ型を簡潔な構文で作成でき、データ指向の型に役立つ動作が組み込みでサポートされているので、データモデルを簡潔に定義できます。
 

値の等価性

レコード構造体(レコードクラスも含む)は等価性に特徴があります。

  • record class 型の場合、型が同じで同じ値が格納されていれば、2 つのオブジェクトは等しい。
  • record struct 型の場合、型が同じで同じ値が格納されていれば、2 つのオブジェクトは等しい。

比較として、レコード型ではないクラスや構造体の等価性は以下の通りです。

  • class 型の場合、メモリ内の同じオブジェクトを参照していれば、2 つのオブジェクトは等しい。
  • struct 型の場合、型が同じで同じ値が格納されていれば、2 つのオブジェクトは等しい。

 

コード
// レコード構造体
PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonStruct person02 = new("太郎", "山田", new DateOnly(1990, 4, 15));

Console.WriteLine("レコード構造体");
Console.WriteLine(person01 == person02);


// レコードクラス
PersonClass person03 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonClass person04 = new("太郎", "山田", new DateOnly(1990, 4, 15));

Console.WriteLine("レコードクラス");
Console.WriteLine(person03 == person04);

record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);
record class PersonClass(string FirstName, string LastName, DateOnly Birthday);

 

実行結果
レコード構造体
True
-----------
レコードクラス
True

 

表示用の組み込みの書式設定

レコード型では既定で ToString メソッドで、パブリック プロパティとフィールドの名前と値が表示されます。
これは、データを取り扱う型としてはとても便利です。

<record type name> { <property name> = <value>, <property name> = <value>, ...}

 

コード
PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
Console.WriteLine(person01);

record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);

 

実行結果
PersonStruct { FirstName = 太郎, LastName = 山田, Birthday = 4/15/1990 }

 

with 式

with 式を使うと、元のインスタンスの値はそのままに、指定したプロパティとフィールドが変更された、コピーを作成できます。
膨大なプロパティを持つレコード型の一部のプロパティだけを変更したコピーを簡潔な構文で作成できるので非常に便利です。

補足ですが、C#10 以降ではレコード型ではない、struct 型も with 式 をサポートしています。
 

コード
PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonStruct person02 = new("太郎", "山田", new DateOnly(1990, 4, 15));

person02 = person01 with { FirstName = "花子" };
Console.WriteLine(person02);
Console.WriteLine(person01 == person02);

record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);

 

実行結果
PersonStruct { FirstName = 花子, LastName = 山田, Birthday = 4/15/1990 }
False

 
なお、レコード構造体はC# 9 のクラスベースのレコードと似ていますが、重要な違いがあります。

  • レコード構造体のプロパティは、デフォルトで変更可能(get / set)です。
  • レコードクラスのプロパティは、デフォルトでは不変(get / init)です

初期化後に init キーワードが設定されているプロパティを再割り当てしようとすると、コンパイルエラーが発生します。
※readonly キーワードを追加することでレコード構造体を不変にできます。

レコード構造体はレコード クラスに取って代わるものではなく、レコード クラスからレコード構造体への移行を推奨するものではありませんので、クラスと構造体のどちらのレコードを使用するかは、利用シナリオに合わせ選択する必要があります。
 
公式情報:
Record structs
レコード (C# リファレンス)