C# のレコード型の概要
C# のレコードとは、データ モデルを操作するための特殊な構文と動作を提供するクラスまたは構造体です。 record
修飾子を使うと、データの格納が主な役割である型に役立つメンバーを合成するようにコンパイラに指示できます。 これらのメンバーには、ToString() のオーバーロードと、値の等価性をサポートするメンバーが含まれます。
レコードを使用する場面
次のシナリオでは、クラスまたは構造体の代わりにレコードを使用することを検討してください。
- 値の等価性に依存するデータ モデルを定義する。
- オブジェクトが不変となる型を定義する。
値の等価性
レコードにおける値の等価性とは、レコード型の 2 つの変数が等しいと見なされるのは、それぞれの型が一致し、かつすべてのプロパティおよびフィールドの値の比較結果が等しい場合であることを意味します。 クラスなどの他の参照型の場合、等価性が既定で意味するのは参照の等価性です。ただし、これは値の等価性が実装されなかった場合に限ります。 つまり、クラス型の 2 つの変数は、同じオブジェクトを参照する場合、等しいことになります。 2 つのレコード インスタンスが等しいかどうかを判断するメソッドと演算子では、値の等価性が使用されます。
すべてのデータ モデルが値の等価性に適しているわけではありません。 たとえば、Entity Framework Core では、概念的に 1 つのエンティティであるものに対して、エンティティ型の 1 つのインスタンスだけが確実に使用されるようにするために、参照の等価性に依存します。 このため、レコード型は Entity Framework Core でエンティティ型として使用するのに適していません。
不変性
不変型とは、オブジェクトがインスタンス化された後にそのプロパティまたはフィールドの値を変更できないようにするためのものです。 不変性は、型をスレッドセーフにする必要がある場合またはハッシュ テーブル内で変化のないハッシュ コードに依存している場合に役立ちます。 レコードには、不変型を作成および操作するための簡潔な構文が用意されています。
不変性は、すべてのデータ シナリオに適しているわけではありません。 たとえば、Entity Framework Core では、不変のエンティティ型を使用した更新がサポートされていません。
レコードと、クラスおよび構造体との違い
クラスまたは構造体を宣言およびインスタンス化するのと同じ構文をレコードでも使用できます。 class
キーワードを record
に置き換えたり、struct
ではなく record struct
を使用したりするだけです。 同様に、継承関係を表す場合も同じ構文がレコード クラスによってサポートされています。 レコードは次の点がクラスとは異なります。
- プライマリ コンストラクターで位置指定パラメーターを使うと、不変プロパティを持つ型を作成してインスタンス化することができます。
- クラスで参照の等価性または非等価性を示すメソッドと演算子 (Object.Equals(Object) や
==
など) は、レコードでは値の等価性または非等価性を示します。 with
式を使用すれば、選択したプロパティに新しい値を指定して、不変オブジェクトのコピーを作成することができます。- レコードの
ToString
メソッドを使用すると、オブジェクトの型名とそのすべてのパブリック プロパティの名前および値を示す書式設定された文字列が作成されます。 - レコードは、別のレコードから継承できます。 レコードはクラスから継承できません。また、クラスはレコードから継承できません。
レコード構造体は、コンパイラによって同等のメソッドおよび ToString
が合成されるという点で構造体とは異なります。 位置指定のレコード構造体の場合、コンパイラによって Deconstruct
メソッドが合成されます。
コンパイラにより、record class
の各プライマリ コンストラクター パラメーターに対して、パブリック初期化専用プロパティが合成されます。 record struct
の場合、コンパイラにより、パブリック読み取り/書き込みプロパティが合成されます。 record
修飾子を含まない class
型と struct
型のプライマリ コンストラクター パラメーターの場合、コンパイラによってプロパティは作成されません。
例
次の例では、位置指定パラメーターを使用してレコードを宣言およびインスタンス化するパブリック レコードを定義します。 次に、型名とプロパティ値を出力します。
public record Person(string FirstName, string LastName);
public static class Program
{
public static void Main()
{
Person person = new("Nancy", "Davolio");
Console.WriteLine(person);
// output: Person { FirstName = Nancy, LastName = Davolio }
}
}
次の例では、レコードでの値の等価性を示します。
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static class Program
{
public static void Main()
{
var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True
Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
}
}
次の例では、with
式を使用して、不変オブジェクトをコピーし、プロパティの 1 つを変更する方法を示します。
public record Person(string FirstName, string LastName)
{
public required string[] PhoneNumbers { get; init; }
}
public class Program
{
public static void Main()
{
Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
Console.WriteLine(person1);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Person person2 = person1 with { FirstName = "John" };
Console.WriteLine(person2);
// output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { PhoneNumbers = new string[1] };
Console.WriteLine(person2);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { };
Console.WriteLine(person1 == person2); // output: True
}
}
詳細については、「レコード (C# リファレンス)」を参照してください。
C# 言語仕様
詳細については、「C# 言語の仕様」を参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。
.NET