【C#】レコード型(record)の基本とクラス/構造体との違い

C# 9.0 以降では「レコード型(record)」が導入されました。レコード型は主に「データを保持するための型」として設計されており、イミュータブル(変更不可)なオブジェクトや値の比較を簡潔に扱えるのが特徴です。本記事ではレコード型の基本と、従来のクラスや構造体との違いを解説します。

レコード型とは?

レコード型は、クラスや構造体のようにデータをまとめる仕組みですが、データ中心の設計に特化しています。特に次の点が特徴です。

  • イミュータブルなデータモデルを簡単に定義できる
  • 値の比較が自動で実装される(値比較)
  • 簡潔な構文で定義できる

基本的な定義方法

public record Person(string Name, int Age);

class Program
{
    static void Main()
    {
        var p1 = new Person("Taro", 20);
        var p2 = new Person("Taro", 20);

        Console.WriteLine(p1 == p2); // True(値が同じなら等しい)
    }
}

クラスではデフォルトで「参照の一致」が比較されますが、レコード型では「値の一致」で比較される点が大きな違いです。

イミュータブルなデータ

レコード型は通常、プロパティが init アクセサを持つため、オブジェクト生成時にのみ値を設定できます。これにより、生成後に値を変更できない安全なデータ構造を作成できます。

public record Product
{
    public string Name { get; init; }
    public int Price { get; init; }
}

class Program
{
    static void Main()
    {
        var p = new Product { Name = "Book", Price = 1000 };
        // p.Price = 2000; // エラー: init プロパティは変更できない
    }
}

with式によるコピー

レコード型は with 式を使って、一部の値を変更した新しいインスタンスを簡単に作成できます。

var p1 = new Person("Hanako", 25);
var p2 = p1 with { Age = 26 };

Console.WriteLine(p1); // Person { Name = Hanako, Age = 25 }
Console.WriteLine(p2); // Person { Name = Hanako, Age = 26 }

元のオブジェクトを保持したまま、新しいオブジェクトを効率よく作成できます。

クラスとの違い

  • クラス: デフォルトでは参照型で、等価比較は参照の一致
  • レコード型: デフォルトで値比較を実装し、データモデルに適する

クラスでも値比較を実装できますが、Equals()GetHashCode() を自分で書く必要があります。レコード型はこれを自動生成してくれる点で便利です。

構造体(struct)との違い

  • 構造体: 値型(スタックに割り当てられることが多い)、小規模データに適する
  • レコード型: クラスベース(参照型)が基本。ただし record struct として値型レコードも定義可能
public record struct Point(int X, int Y);

var p1 = new Point(1, 2);
var p2 = new Point(1, 2);

Console.WriteLine(p1 == p2); // True(値が同じなら等しい)

まとめ

C# のレコード型は「データモデルの表現」を簡潔かつ安全にするための仕組みです。

  • 値比較やイミュータブル設計が標準でサポートされる
  • with 式で簡単にコピー&変更が可能
  • クラスや構造体よりも「データ中心の設計」に適している

DTO(データ転送オブジェクト)や設定値オブジェクトなど、値の比較や変更不可が求められるケースで活用するのがベストプラクティスです。