等価性とハッシュ(値と参照)
参照の等価性と値の等価性を理解し、Equals/GetHashCodeを正しくオーバーライドし、IEquatable を実装し、集合や辞書でカスタム比較子を使います。
「等価性とハッシュ(値と参照)」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全3レッスンが含まれています。
等価性の基本
目標: 等価性が期待どおりに機能するようにします。
- 参照等価性と値等価性
- Equals と GetHashCode のコントラクト
- 高速化のための IEquatable<T>
- 型を変更できない場合のカスタム比較子
参照等価性の落とし穴
クラスはデフォルトでは参照等価性を使用します。値等価性を実装しない限り、同じデータを持つ別々のオブジェクトは等しいとはみなされません。
using System;
using System.Collections.Generic;
public sealed class Point // no overrides
{
public int X;
public int Y;
public Point(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
Point a = new Point(1, 2);
Point b = new Point(1, 2);
Console.WriteLine("a == b ? " + (a == b)); // reference equality: False
Console.WriteLine("a.Equals(b) ? " + a.Equals(b)); // False
HashSet<Point> set = new HashSet<Point>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // False (unexpected)
}
}
値等価性の実装
IEquatable<T> を実装し、Equals と GetHashCode をオーバーライドします。ハッシュ値は等価性と一致している必要があります。等しいオブジェクトは同じハッシュ値になります。
using System;
using System.Collections.Generic;
public sealed class ValuePoint : IEquatable<ValuePoint>
{
public int X;
public int Y;
public ValuePoint(int x, int y){ X = x; Y = y; }
public bool Equals(ValuePoint other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
return Equals(obj as ValuePoint);
}
public override int GetHashCode()
{
// Simple, stable combination (avoid randomness)
unchecked
{
int hash = 17;
hash = hash * 31 + X.GetHashCode();
hash = hash * 31 + Y.GetHashCode();
return hash;
}
}
}
public class Program
{
public static void Main(string[] args)
{
ValuePoint a = new ValuePoint(1, 2);
ValuePoint b = new ValuePoint(1, 2);
Console.WriteLine("a.Equals(b)? " + a.Equals(b)); // True
HashSet<ValuePoint> set = new HashSet<ValuePoint>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // True (value semantics)
}
}
カスタム比較子
型自体を変更できない場合は、カスタム IEqualityComparer<T> を HashSet/Dictionary に渡します。
using System;
using System.Collections.Generic;
public sealed class Person // imagine from a library; cannot edit
{
public string Name;
public int BirthYear;
public Person(string name, int year){ Name = name; BirthYear = year; }
}
public sealed class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person a, Person b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Name == b.Name && a.BirthYear == b.BirthYear;
}
public int GetHashCode(Person p)
{
if (ReferenceEquals(p, null)) return 0;
unchecked
{
int h = 23;
h = h * 31 + (p.Name == null ? 0 : p.Name.GetHashCode());
h = h * 31 + p.BirthYear.GetHashCode();
return h;
}
}
}
public class Program
{
public static void Main(string[] args)
{
Person p1 = new Person("Ada", 1815);
Person p2 = new Person("Ada", 1815);
HashSet<Person> set = new HashSet<Person>(new PersonComparer());
set.Add(p1);
Console.WriteLine("Contains p2? " + set.Contains(p2)); // True via comparer
}
}
コントラクトと落とし穴
ルール:
- Equals(a,b) が true ならば ⇒ GetHashCode(a) == GetHashCode(b)
- 等価性は反射的、対称的、推移的である必要があります
- 可能な限り、ハッシュ計算には不変フィールドを使用します
- GetHashCode でランダムな値を使用しないでください
構造体とクラスに関する注意
構造体はデフォルトではフィールドによって比較されます(値セマンティクス)。クラスは値等価性を実装しない限り、参照によって比較されます。
using System;
public struct PointS // struct: value type
{
public int X;
public int Y;
public PointS(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
PointS a = new PointS(1, 2);
PointS b = new PointS(1, 2);
Console.WriteLine("Struct equality: " + a.Equals(b)); // True by default (field-wise)
}
}
コレクションでの等価性コントラクト
まとめ
まとめ: クラスで値セマンティクスを実現するには、Equals/GetHashCode(および多くの場合 IEquatable<T>)が必要です。型を編集できない場合はカスタム比較子を使用します。
よくある質問
「等価性とハッシュ(値と参照)」レッスンは無料ですか?
はい。「等価性とハッシュ(値と参照)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全3レッスンが含まれています。
「等価性とハッシュ(値と参照)」で何を学びますか?
参照の等価性と値の等価性を理解し、Equals/GetHashCodeを正しくオーバーライドし、IEquatable を実装し、集合や辞書でカスタム比較子を使います。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。
「等価性とハッシュ(値と参照)」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。