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# リファレンス)