C#のrecordとclassの違い!不変なデータモデルの使い分け

[PR]

C#

最新情報に基づき、C#のrecordとclassの違いがどこにあるのか、なぜrecordが近年注目されているのか、実際の使いどころを含めて詳しく解説します。recordとclassの基本的な振る舞いの差異から、immutability、比較、継承、パフォーマンス、シリアライズとの相性まで幅広く扱いますので、C#で「record class 違い」を調べている方にとって満足度の高い内容となるはずです。コード例や比較表も豊富に盛り込みますので、すぐに自分のプロジェクトで使い分けられるようになります。

C# record class 違い:recordとclassの基本的な定義と目的の違い

recordとclassはいずれもC#で型を定義する基本的な手段ですが、それぞれ担当する役割が異なります。recordはデータモデルとしての使用が主目的で、classは動作や振る舞い(メソッド、状態の変更、継承など)を持つオブジェクト指向設計で使用されます。recordは主に値に基づく等価性や不変性を提供し、ボイラープレートコードを減らす機能を備えています。classはこれらの機能がデフォルトでは備わっておらず、必要に応じて開発者が設計する必要があります。

recordとは何か:特性と機能

recordはC# 9で導入されたデータ中心の型で、主にデータの内容が等価性や表示、コピーなどの操作において重要になるケースで使われます。具体的には、
・プロパティに基づく値比較(Equals/==演算子の自動生成)
・ToStringの自動実装によるデバッグやログの改善
・with式を用いた非破壊的な更新(元のインスタンスを変更せず新しいインスタンスを作る)
などの機能を持ち、コードの簡潔さと明瞭さを高めます。

classとは何か:一般的な用途と思考モデル

classはC#で古くから使われている参照型の基本で、オブジェクトの振る舞い(メソッド)、状態の可変性、参照の等価性などを扱うための柔軟性を持ちます。状態を内部で変更することが前提の設計や、IDで識別されるエンティティ、業務ロジックを持つオブジェクトなどに向いています。classを用いると、等価性やToStringなどはデフォルトでは参照型のものとなるため、必要なら手動でオーバーライドします。

値型・参照型との関係:record classとrecord struct

recordは「record class」と「record struct」の2種類に分かれます。record classは参照型として動作し、record structは値型です。record classではプロパティがinit-only(不変)であることが多く、record structではmutableなプロパティが許されるものがあります。readonly record structであれば、構造体にもかかわらず不変性を強制できます。こうした区別によりデータのコピーや等価性の定義方法が変わります。

機能比較で見るrecordとclassの違い

recordとclassの違いを正確に把握するには、等価性、可変性、継承、デフォルトの文字列化、コピー動作などの観点で比較することが重要です。以下に最新情報に基づいた比較表を示します。

機能 record(record class) class
等価性 (Equality) 値に基づく等価性が自動生成される(プロパティごとに比較) デフォルトは参照等価性。同内容でも異なるインスタンスなら等しくない
可変性 (Mutability) 主に不変(init-onlyプロパティ);mutableに変更可能 デフォルトで可変;必要なら不変を設計可能
コピーと更新(非破壊的変更) with式によるコピーと更新が簡単に可能 コピー動作や更新は手動で実装する必要がある
ToStringのデフォルト 型名とプロパティ名と値を列挙する形式が自動生成される デフォルトは型名のみ。プロパティ値を表示するにはオーバーライドが必要
継承 (Inheritance) record classはrecord classから継承可能。runtime型も考慮される等価性 一般的なクラスの継承。柔軟だが設計に注意が必要

等価性(Equality)の比較

recordではプロパティの内容によって等価性が自動的に判定されます。record classなら複数のインスタンスでも中身が同じであれば==演算子がtrueを返します。classは参照先が同じかどうかで判断されます。等価性の比較が重要な場合、recordが非常に便利です。最新のC#ではこの機能が標準搭載されており、パフォーマンスも意識された実装です。

可変性(Mutability)の比較

record classのプロパティは通常init-onlyで定義され不変となります。不可変な状態を保つことでスレッドセーフティやバグの発生を抑制できます。classは通常get/setで状態変更可能です。不変性を持たせたい場合にはreadonlyやimmutableな設計を自ら行う必要があります。

コピーとwith式の利用

recordはwith式によって、既存インスタンスをベースに一部プロパティだけを変えた新しいインスタンスを簡単に作れます。これによって不変性を保ちながら変更のあるデータを扱うことが可能です。classではクローンパターンや手動でプロパティをコピーする方法を取らなければならず、冗長になりがちです。

使い分けの基準:recordを選ぶべき場面とclassが適している場面

どちらかを選ぶときに迷ったら、用途や設計の意図に応じて判断基準を持つことが重要です。以下にrecordが向いているケースとclassが向いているケース、それぞれについて最新のベストプラクティスにも言及しながらまとめます。

recordを使うべきケース

recordは値中心のデータ表現が必要なときに最適です。具体的には、
・DTOやAPIのリクエスト・レスポンスモデル
・設定オブジェクトや構成スナップショット
・ドメイン駆動設計での値オブジェクト(Value Object)
・イミュータブルな状態を持つアプリケーション状態管理
などです。値の等価性を重視し、オブジェクトの実体そのものより中身が重要な設計に向いています。

classを使うべきケース

classが向いているのは、オブジェクトの身元(Identity)が重要な場面です。たとえば、データベースのエンティティ、UI要素、状態が頻繁に変わるモデル、複雑なメソッドや振る舞いを多く持つオブジェクトなどです。mutableなプロパティを持ち、参照等価性が自然なドメインではclassの方が適切です。

パフォーマンスとメモリの観点からの判断

record structを使うと値型としてスタックに割り当てられるなどの利点がありますが、コピーコストやメモリの扱いが影響することがあります。record classはヒープ上に配置され、ガーベジコレクションの対象となります。頻繁に生成/破棄する小さな型ではrecord structが効果的ですが、深い継承階層や豊富な振る舞いを持たせるならclassの方が無難です。

言語仕様と最新情報:C#で変わったrecordの取り扱い

C#のバージョンが進むにつれてrecordには新しい括りや修飾子が追加され、classとの違いがより明確になっています。最新情報にもとづいて、recordに関する最新の言語仕様の変化とその意味を整理します。

record structとreadonly record structの導入

C# 10以降でrecord structが導入され、構造体でもrecordの機能が使えるようになりました。record structではmutableなプロパティが許され、readonly record structを使えば構造体でも不変性を厳格に保てます。値型としての効率性とrecordの利便性を組み合わせたものです。

継承と型のランタイム等価性

record class同士の継承が可能で、等価性比較にはランタイムの型が含まれます。たとえ共通の基底recordを持っていても、派生型と基底型のインスタンスは同じプロパティ値でも等価とは見なされません。これにより型安全性が高まり、予期せぬ等価性の混同を防げます。

シリアライズの挙動と互換性

最新のシリアライズライブラリではrecordとの相性が良く、特にJSONシリアライズにおいて init-onlyプロパティや非破壊的コピーといったrecordの特徴をサポートするものが増えています。一方でいくつかのフレームワーク(例としてORM系)では、recordの不変性やパラメータ付きコンストラクタの存在が適切にサポートされないケースがあるため注意が必要です。

具体的なコード例で見るrecordとclassの違い

実際にどのようなコードを書くとrecordとclassの挙動が異なるのかをいくつか例示します。コードに触れることで違いを体感できるようになります。

等価性と参照の比較例

record class同士を==で比較するとき、中身が同じならtrueになります。class同士ではインスタンスが別ならfalseです。
例として、record Personとclass PersonClassを用意すると、record は内容一致で等しいと評価されます。class は参照先が異なれば異なります。こうしたコードをテストやキャッシュキーの設計で活かせます。

with式を使った非破壊的コピー

recordではwith式を使って元のインスタンスを保持しつつ、一部を変えた新しいインスタンスを生成できます。mutableなclassではこのような操作を自前で実装するか、既存のプロパティを直接変更する必要があります。変更履歴や不変性を保ちたい場合、recordのwith式が非常に有用です。

raw mutableプロパティやrecord structの例

record structを使うと、mutableなプロパティを含む構造体としてrecordの機能を活かせます。readonly record structなら完全な不変モデルになります。classではmutableプロパティが一般的ですが、不変性が必要な場面ではreadonly修飾やimmutable設計を使うことがあります。

よくある誤解と注意点:record class 違いで押さえるべきポイント

誤解が起こりやすい部分を整理します。recordが万能ではなく、classでもrecordでも設計次第で注意すべき側面があります。誤った使い方や落とし穴を理解することで、適切な選択ができます。

mutable recordは不変性を損なうことがある

recordにしてもプロパティをsettableにすれば可変になります。その場合、値ベース等価性の前提が崩れることがあります。不変であることを信頼して設計している部分で予期しない動作を起こすことがあるので、mutableなrecord使用時には特に注意が必要です。

Entity Framework CoreなどORMとの相性

ORMではオブジェクトの追跡やプロキシ生成などで参照型とパラメータレスコンストラクタなどの要件が強く作用します。recordを使ったモデルでパラメータ付きコンストラクタしか持たない場合、ORMによっては正しくマッピングできないケースがあります。こういった場合はclassかrecordでもパラメータレスコンストラクタを持たせるなどの工夫が必要です。

等価性のシャローコピーとディープコピーの違い

recordのコピー機構(with式)は shallow copyに近い動作をします。内部に参照型のプロパティを持っていると、その参照先の共有が発生します。完全なディープコピーをしたい場合には手動で実装するか、参照型メンバのコピーを意識する必要があります。

まとめ

recordとclassの違いを整理すると、recordは主にデータの中身が重要で不変性や比較性を自動化したいときの型であり、classはオブジェクトの振る舞い、可変性、身元(identity)が重要なときに適している型です。
record classとrecord structを使い分ければ、値型・参照型それぞれのメリットを得られます。

実践的な使い分けのヒントとしては、
・APIモデルや値オブジェクトではrecordを採用する
・ビジネスロジックやUIバインディング、エンティティではclassを採用する
・パフォーマンスが重要なループや大量生成環境ではrecord structを検討する
・ORMやシリアライザとの互換性を確認する

これらをふまえて、あなたのプロジェクトでrecord class 違いを正しく使い分けることで、コードの可読性、保守性、バグの起きにくさが向上しますので、設計の初期段階でどちらを使うか意図を明確にしておくことが重要です。

関連記事

特集記事

コメント

この記事へのトラックバックはありません。

TOP
CLOSE