ホームに戻る
出典 :
関連 :
目次 :
レコード型
C#9.0で追加されたデータ型。「値の等価性をサポートする参照型」であり、クラスと構造体の双方の特徴を取り入れている。
Javaにおけるレコードと共通の概念。
「値の等価性」とは
オブジェクト自身の型とすべてのメンバの値が等しい場合に、2つのオブジェクトが等しいとすること。
対義語として「参照の等価性」があり、これは2つのオブジェクトが同一のオブジェクトを指す場合に等しいとすることを指す。
// クラス( class )による実装
public class PersonClass
{
// プロパティ
public string LastName { get; }
public string FirstName { get; }
// コンストラクタ
public PersonClass(string first, string last) => (FirstName, LastName) = (first, last);
}
// 構造体( struct )による実装
public struct PersonStruct
{
// プロパティ
public string LastName { get; }
public string FirstName { get; }
// コンストラクタ
public PersonStruct(string first, string last) => (FirstName, LastName) = (first, last);
}
// レコード( record )による実装
public record PersonRecord
{
// プロパティ
public string LastName { get; }
public string FirstName { get; }
// コンストラクタ
public PersonRecord(string first, string last) => (FirstName, LastName) = (first, last);
}
:
{
// PersonClass 型の比較
var personClass1 = new PersonClass("Bill", "Wagner");
var personClass2 = new PersonClass("Bill", "Wagner");
Console.WriteLine( personClass1.Equals(personClass2) );
// PersonStruct 型の比較
var personStruct1 = new PersonStruct("Bill", "Wagner");
var personStruct2 = new PersonStruct("Bill", "Wagner");
Console.WriteLine( personStruct1.Equals(personStruct2) );
// PersonRecord 型の比較
var personRecord1 = new PersonRecord("Bill", "Wagner");
var personRecord2 = new PersonRecord("Bill", "Wagner");
Console.WriteLine( personRecord1.Equals(personRecord2) );
}
実行結果 :
false //< PersonClass 同士の Equals()
true //< PersonStruct 同士の Equals()
true //< PersonRecord 同士の Equals()
上記のコードのようにオブジェクトの比較を行った場合、クラス( class )は2つのオブジェクトが同一でなければ等しいとは見なされないが、
構造体( struct )およびレコード( record )では、オブジェクトの型とすべてのメンバの値が等しければ等しいとされる。
クラスで値の等価性を確保しようとした場合 Equals() および GetHashCode() のオーバーライドが必要であるが、レコードはそれらが自動的に実装される。
構造体( struct )との違い
構造体は値型だが、レコードはクラスと同様の参照型である。
構造体と異なり、以下の特徴を持つ。これらはいずれもクラスと共通である。
- 参照渡しとなる
- NULLを格納できる
- 継承がサポートされる
|
クラス ( class ) |
構造体 ( struct ) |
レコード ( record ) |
型 |
参照 |
値 |
参照 |
等価性 |
参照 |
値 |
値 |
レコードの存在意義
レコードは主にデータ(メソッドではない)の格納に用いることが想定されている。
値の等価性に依存するデータモデルを定義でき、不変(イミュータブル)なオブジェクトを簡便に構築できる。
クラス同様参照型であることから、メソッドの引数に用いた場合もオーバーヘッドが小さい。
プライマリコンストラクタによる初期化
C#12以降、クラス、構造体、レコードでプライマリコンストラクタが宣言できるようになった。
レコードにプライマリコンストラクタを宣言した場合、コンストラクタ引数と同名のパブリックプロパティが生成される。
(プライマリコンストラクタの引数は「位置パラメータ」、生成されるプロパティは「位置プロパティ」と呼ばれる。)
これにより、イミュータブルなレコードを簡潔に定義することができる。
// プライマリコンストラクタを用いた Person レコードの定義
public record Person(string FirstName, string LastName);
これは以下と等価である。
// プライマリコンストラクタを用いた Person レコードの定義
public record Person(string FirstName, string LastName)
{
public required string FirstName { get; init; } = FirstName;
public required string LastName { get; init; } = LastName;
}
required 修飾は、そのメンバがインスタンスの生成時に必ず初期化されることを強制する。
これにより、レコード Person の各メンバは必ず値を持つことが保証され、かつ不変となる( set アクセサが定義されないため)。
プライマリコンストラクタを宣言した場合、引数はすべてのインスタンスの生成に必要であるものと見なされ、
他のコンストラクタは this(...) 構文を用いてプライマリコンストラクタを呼び出す必要がある。
また、プロパティの一部のみを独自に定義することも可能である。
public record Person(string FirstName, string LastName, string Id)
{
internal string Id { get; init; } = Id;
}
ここでは、FirstName 、LastName が public プロパティとして自動生成されるが、Id は internal として定義される。
with 式
with 式を用いることで、プロパティの一部のみ値が異なる複製オブジェクトを作成できる。
with 式は、init アクセサが定義されている場合のみ使用可能。
{
var person1 = new Person("Terry", "Bogard"); //< FirstName = "Terry" 、LastName = "Bogard" で初期化
var person2 = person1 with { FirstName = "Andy" }; //< FirstName のみを "Andy" に変更
}
ToString()
レコードではクラス同様、ToString() が自動的に生成される。
ToString() をオーバーライドしない場合、クラスでは型名が出力されるが、レコードでは型名、メンバ名とそれぞれの値が出力される。
{
// PersonClassToString()
Console.WriteLine(personClass1.ToString());
// PersonRecord.ToString()
Console.WriteLine(personRecord1.ToString());
}
実行結果 :
PersonClass
PersonRecord { LastName = Wagner, FirstName = Bill }
record class と record struct
C#10では record struct が追加された。機能は record と同様であるが、値型である点が異なる。
同様に参照型であることを明示するための record class も追加されており、これは record と同義である。
即ち、record は record class の省略形と見ることができる。詳細はリンク先を参照。