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# の構文と使用法に関する信頼性のある情報源です。