record と class で == の結果が変わる理由
recordはプロパティの値が全て同じなら==がtrueになる値ベースの等価性を自動生成します。classは既定で参照ベースの等価性のままです。
なぜエラーが出ないのか
出力: False
(エラーなし)- C#は何も報告しません。文法として正しいためです
出力: False- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 class Point(int X, int Y); ^ 2 class Program { 3 static void Main(string[] args) { 4 Point a = new Point(1, 2); 5 Point b = new Point(1, 2); 6 System.Console.WriteLine(a == b); 7 } 8 }
recordは既定で値(プロパティの中身)を比較する==を自動生成します。classは参照を比較するので、同じ値でもfalseになります。
直し方: class を record にします。
パターン2
1 class Money(decimal Amount, string Currency); ^ 2 class Program { 3 static void Main(string[] args) { 4 Money a = new Money(1000, "JPY"); 5 Money b = new Money(1000, "JPY"); 6 System.Console.WriteLine(a == b); 7 } 8 }
金額のように「値」として扱いたいデータはrecordにすると、比較のたびにEqualsを書かずに済みます。
直し方: class を record にします。
パターン3
1 class Range(int Start, int End); ^ 2 class Program { 3 static void Main(string[] args) { 4 Range a = new Range(0, 10); 5 Range b = new Range(0, 10); 6 System.Console.WriteLine(a == b); 7 } 8 }
区間のような不変のデータもrecordに向いています。プロパティが全て同じなら等しいと判定されます。
直し方: class を record にします。
パターン4
1 class Pair(string A, string B); ^ 2 class Program { 3 static void Main(string[] args) { 4 Pair a = new Pair("x", "y"); 5 Pair b = new Pair("x", "y"); 6 System.Console.WriteLine(a == b); 7 } 8 }
recordは各プロパティの値が全て同じなら等しいと判定します。classだと参照比較になり同じ値でもfalseです。
直し方: class を record にします。
パターン5
1 class Coord(double Lat, double Lng); ^ 2 class Program { 3 static void Main(string[] args) { 4 Coord a = new Coord(35.0, 139.0); 5 Coord b = new Coord(35.0, 139.0); 6 System.Console.WriteLine(a == b); 7 } 8 }
座標のような値データもrecordにすると、値の比較が自然に書けます。
直し方: class を record にします。
よくある誤解
見た目がほとんど同じrecordとclassの宣言でも、==の意味は正反対になります。値として比較したいデータ構造にはrecordが向いています。
実務での勘所
recordが自動生成するのはEquals・GetHashCodeだけではありません。全プロパティを見やすく整形したToString()や、位置パターンマッチ(var (x, y) = pointのような書き方)を可能にするDeconstructメソッドも、コンパイラが自動的に用意してくれます。Equals・GetHashCode・IEquatable<T>を自分で正しく実装する手間を、recordを使うだけでまとめて解消できる、という関係にあります。単純にデータを保持するだけの型を書くときは、classより先にrecordを検討する価値があります。