0Pricing
C# Academy · レッスン

IEquatableの実装

Equalsで型安全な等価性を提供します。

「IEquatableの実装」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

なぜ IEquatable<T> なのか

IEquatable<T> は、型安全な Equals(T other) メソッドを提供します。値型ではボックス化を、参照型ではキャストを避けられるため、等価性判定をより高速かつ明確にできます。

インターフェイスを実装する

IEquatable<T> を実装し、Equals(T other) を用意してください。また、すべての経路で一貫性を保つため、継承した object.Equals と GetHashCode もオーバーライドする必要があります。

using System;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }

    public bool Equals(Point other) => X == other.X && Y == other.Y;
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Point(1, 2).Equals(new Point(1, 2)));
    }
}

構造体のボックス化を避ける

非ジェネリックな object.Equals(object) は、構造体の引数をボックス化します。IEquatable<T> による型付きの Equals(Point other) は構造体を直接受け取るため、割り当ては発生しません。

using System;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }
    public bool Equals(Point other) => X == other.X && Y == other.Y; // no boxing
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        Point a = new Point(3, 4);
        Point b = new Point(3, 4);
        Console.WriteLine(a.Equals(b)); // calls the typed overload
    }
}

コレクションは IEquatable<T> を使用する

List<T>.Contains、Dictionary、HashSet は、利用できる場合に IEquatable<T>.Equals を優先します。これを実装すると、これらの操作を自分の型に対して効率的かつ正しく実行できます。

using System;
using System.Collections.Generic;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }
    public bool Equals(Point other) => X == other.X && Y == other.Y;
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        var list = new List<Point> { new Point(0, 0), new Point(1, 1) };
        Console.WriteLine(list.Contains(new Point(1, 1)));
    }
}

参照型と IEquatable<T>

クラスも IEquatable<T> を実装できます。型付きの Equals を使えばキャストを避けられ、参照型の引数については null を明示的に処理します。

using System;

public class Person : IEquatable<Person>
{
    public string Name;
    public Person(string name) { Name = name; }

    public bool Equals(Person other) => other != null && other.Name == Name;
    public override bool Equals(object obj) => Equals(obj as Person);
    public override int GetHashCode() => Name?.GetHashCode() ?? 0;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Person("Ada").Equals(new Person("Ada")));
        Console.WriteLine(new Person("Ada").Equals((Person)null));
    }
}

object.Equals を型付きメソッドに委譲する

比較処理を Equals(T) に一度だけ記述し、object.Equals からそれに委譲するのが、すっきりしたパターンです。これにより、両者が同じ動作をすることを保証できます。

using System;

public struct Id : IEquatable<Id>
{
    public int Value;
    public Id(int value) { Value = value; }
    public bool Equals(Id other) => Value == other.Value;
    public override bool Equals(object obj) => obj is Id other && Equals(other);
    public override int GetHashCode() => Value;
}

public class Program
{
    public static void Main()
    {
        object boxed = new Id(5);
        Console.WriteLine(boxed.Equals(new Id(5)));
    }
}

複数のフィールドを比較する

複数のフィールドを持つ型では、Equals ですべてのフィールドを比較してください。関係するすべてのフィールドが一致する場合に限り、2つのインスタンスは等しいとみなされます。

using System;

public struct DateRange : IEquatable<DateRange>
{
    public int Start, End;
    public DateRange(int start, int end) { Start = start; End = end; }
    public bool Equals(DateRange other) => Start == other.Start && End == other.End;
    public override bool Equals(object obj) => obj is DateRange d && Equals(d);
    public override int GetHashCode() => HashCode.Combine(Start, End);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 5)));
        Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 6)));
    }
}

演算子と組み合わせる

IEquatable<T> を == と != と組み合わせ、どちらの構文でも比較できるようにしてください。どちらも同じ型付き比較処理に基づくようにします。

using System;

public struct Money : IEquatable<Money>
{
    public decimal Amount;
    public Money(decimal a) { Amount = a; }
    public bool Equals(Money other) => Amount == other.Amount;
    public override bool Equals(object obj) => obj is Money m && Equals(m);
    public override int GetHashCode() => Amount.GetHashCode();
    public static bool operator ==(Money a, Money b) => a.Equals(b);
    public static bool operator !=(Money a, Money b) => !a.Equals(b);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Money(9.99m) == new Money(9.99m));
    }
}

レコードは自動的に実装する

C# の record は、IEquatable<T>、値に基づく Equals、GetHashCode を自動生成します。値による等価性だけが必要なら、レコードを使うことで定型コードを減らせます。

using System;

public record Point(int X, int Y);

public class Program
{
    public static void Main()
    {
        var a = new Point(1, 2);
        var b = new Point(1, 2);
        Console.WriteLine(a.Equals(b));
        Console.WriteLine(a == b);
    }
}

手動で実装する場合

大文字と小文字を区別しない名前や特定のフィールドを無視する場合など、レコードでは直接表現できないカスタムの等価性ルールが必要なときは、IEquatable<T> を自分で実装してください。

using System;

public class CaseInsensitiveTag : IEquatable<CaseInsensitiveTag>
{
    public string Name;
    public CaseInsensitiveTag(string name) { Name = name; }
    public bool Equals(CaseInsensitiveTag other)
        => other != null && string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
    public override bool Equals(object obj) => Equals(obj as CaseInsensitiveTag);
    public override int GetHashCode() => Name?.ToLowerInvariant().GetHashCode() ?? 0;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new CaseInsensitiveTag("Red").Equals(new CaseInsensitiveTag("RED")));
    }
}

自分で試してみる

小さな参照型に IEquatable<T> を実装し、すべての等価性判定の経路を型付きメソッドに委譲してみましょう。

using System;
using System.Collections.Generic;

public class Coordinate : IEquatable<Coordinate>
{
    public int Lat, Lng;
    public Coordinate(int lat, int lng) { Lat = lat; Lng = lng; }
    public bool Equals(Coordinate other) => other != null && other.Lat == Lat && other.Lng == Lng;
    public override bool Equals(object obj) => Equals(obj as Coordinate);
    public override int GetHashCode() => HashCode.Combine(Lat, Lng);
}

public class Program
{
    public static void Main()
    {
        var set = new HashSet<Coordinate> { new Coordinate(10, 20) };
        Console.WriteLine(set.Contains(new Coordinate(10, 20)));
    }
}

クイックチェック

IEquatable<T> の利点を思い出してください。

まとめ

IEquatable<T> は型安全な等価性判定を提供します。

  • Equals(T other) を提供し、構造体のボックス化とクラスのキャストを避けられます。
  • コレクションは正確で効率的な検索のためにこれを優先します。
  • object.Equals と演算子から型付きメソッドに処理を委譲します。
  • レコードは自動的に実装します。カスタムルールが必要な場合は手動で実装してください。

よくある質問

「IEquatableの実装」レッスンは無料ですか?

はい。「IEquatableの実装」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「IEquatableの実装」で何を学びますか?

Equalsで型安全な等価性を提供します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「IEquatableの実装」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. IEquatableの実装
  2. GetHashCodeのオーバーライド
  3. IComparableの実装
  4. カスタムソートのためのIComparer
← C# Academyに戻る