C# の record は、主に不変(immutable)なデータ構造を簡潔に定義するための構文です。C# 9.0(.NET 5)で導入され、値の等価性を持ち、データの保持・比較・構造的な等価性を目的としたシナリオで活躍します。
基本構文
最もシンプルな record の定義は次のとおりです。
public record Person(string Name, int Age);
たったこの1行で次のことが自動で行われます。
NameとAgeのプロパティを持つクラスを定義- コンストラクタを自動生成
ToString()/Equals()/GetHashCode()を自動実装withキーワードによるコピー構文が使えるDeconstruct()メソッドを自動生成
値の等価性(Value Equality)
class では Equals() がデフォルトで参照比較になりますが、record はプロパティの中身で比較します。
var person1 = new Person("Alice", 30);
var person2 = new Person("Alice", 30);
Console.WriteLine(person1 == person2); // True(中身が同じなので等価)
Console.WriteLine(ReferenceEquals(person1, person2)); // False(別インスタンス)
同じ値を持つ別インスタンスでも == が true になるのが record の大きな特徴です。
with 式(コピー&変更)
record には with 式があり、一部のプロパティだけ変えた新しいインスタンスを簡単に作れます。
var person1 = new Person("Alice", 30);
var person3 = person1 with { Age = 31 };
Console.WriteLine(person1); // Person { Name = Alice, Age = 30 }
Console.WriteLine(person3); // Person { Name = Alice, Age = 31 }
元のインスタンスは変更されません。イミュータブルなデータを安全に「更新」できます。
ToString() の自動実装
record は ToString() が自動でオーバーライドされ、プロパティ名と値をわかりやすく表示します。
var person = new Person("Alice", 30);
Console.WriteLine(person); // Person { Name = Alice, Age = 30 }
デバッグ時にも便利です。
分解(Deconstruct)
record は Deconstruct() を自動実装するため、タプルのように分解して受け取れます。
var person = new Person("Alice", 30);
var (name, age) = person;
Console.WriteLine(name); // Alice
Console.WriteLine(age); // 30
class との違い
| 比較項目 | class |
record |
|---|---|---|
| 比較方法 | 参照比較 | 値比較(プロパティの中身) |
with コピー構文 |
使えない | 使える |
ToString() 自動実装 |
なし | あり |
Deconstruct() 自動実装 |
なし | あり |
| イミュータブル既定 | なし(手動実装) | あり(init only) |
record struct(C# 10)
C# 10(.NET 6)では record struct も導入されました。クラスベースの record(参照型)と異なり、値型として動作します。
public record struct Point(int X, int Y);
こちらはスタック上に配置されるため、小さく頻繁に生成するデータに向いています。ただし、record struct はデフォルトでミュータブルである点に注意が必要です。
ミュータブルな record
record はデフォルトでイミュータブルですが、set を使うことでミュータブルにすることもできます。
public record MutablePerson(string Name)
{
public int Age { get; set; }
}
ただし、この場合は class との差が薄れるため、通常は init のままイミュータブルで使うのが推奨されます。
主な用途
- API レスポンスのデータモデル
- DTO(Data Transfer Object)
- 状態管理(Redux ライクなパターン)
- コンパクトなデータクラスが欲しいとき
まとめ
C# の record は「データを持つためのクラス」を簡潔かつ安全に定義するための構文です。
classに比べて値の等価性・with式・ToString()の自動実装が得られる- 「中身が同じなら等しい」としたい場面に最適
- C# 10 からは値型の
record structも選択肢になる
タプルと組み合わせるとさらに表現力が上がります。詳しくは [C#] タプル(Tuples)を深掘りする を参照してください。