IEqualityComparer <T>とIEquatable <T>の違いは何ですか?


151

私はどこのシナリオを理解したいIEqualityComparer<T>IEquatable<T>使用する必要があります。両方のMSDNドキュメントは非常によく似ています。


1
MSDN sez:「このインターフェイスでは、コレクションのカスタマイズされた等価比較実装できます」もちろん、これは選択した回答で推定されます。MSDNにも継承推奨していますEqualityComparer<T>ので、「代わりのインタフェースを実装するEqualityComparer<T>テストの平等を使用してIEquatable<T>
radarbob

...上記は、T実装用のカスタムコレクションを作成する必要があることを示していますIEquatable<T>List<T>そうでなければ、コレクションにある種の微妙なバグがあるでしょうか?
レーダーボブ2016


@RamilShavaleevリンクが壊れています
ChessMax

回答:


117

IEqualityComparer<T>タイプの2つのオブジェクトの比較を実行するオブジェクトのインターフェースですT

IEquatable<T>T同じタイプの別のオブジェクトと比較できるように、タイプのオブジェクト用です。


1
同じ違いがIComparable / IComparer
boctulus '19年

3
キャプテン
オブビビッド

(編集後)はIEqualityComparer<T>、オブジェクトのためのインタフェースである(通常は異なる軽量クラスであるT上で動作する比較関数を提供するT
rwong

61

IEquatable<T>またはを使用するかどうかを決定するときにIEqualityComparer<T>、次のように質問できます。

の2つのインスタンスがT等しいかどうかをテストする好ましい方法はありますか、または同等に有効な方法がいくつかありますか?

  • 2つのインスタンスをテストする唯一の方法がある場合はT平等のために、またはいくつかの方法の一つが好まれている場合は、IEquatable<T>正しい選択のようになります。このインタフェースのみで実装されることになっているTので、1つのインスタンスがあること、自身Tの内部知識を持っています自身をの別のインスタンスと比較する方法T

  • 一方、2つTのを比較して同等に妥当な方法がいくつかある場合は、IEqualityComparer<T>より適切に思えます。このインターフェースは、Tそれ自体ではなく、他の「外部」クラスによって実装されることを意図しています。したがって、の2つのインスタンスのT等価性をテストする場合、等価性Tの内部的な理解がないためIEqualityComparer<T>、特定の要件に従ってテストを実行するインスタンスを明示的に選択する必要があります。

例:

次の2つのタイプ(値のセマンティクスを持っていると想定されています)について考えてみましょう。

interface IIntPoint : IEquatable<IIntPoint>
{
    int X { get; }
    int Y { get; }
}

interface IDoublePoint  // does not inherit IEquatable<IDoublePoint>; see below.
{
    double X { get; }
    double Y { get; }
}

これらの型の1つだけIEquatable<>が継承し、他の型は継承しないのはなぜですか?

理論的には、いずれかのタイプの2つのインスタンスを比較する賢明な方法は1つだけです。両方のインスタンスのXおよびYプロパティが等しい場合、それらは等しいです。この考え方によるとIEquatable<>、同等性テストを実行する他の意味のある方法はありそうにないため、どちらのタイプもを実装する必要があります。

ここでの問題は、分数の丸めエラーが原因で、浮動小数点数の等しいかどうかの比較が期待どおりに機能しない可能性があることです。浮動小数点数を同等にするためのさまざまな方法があり、それぞれに特定の利点とトレードオフがあります。適切な方法を自分で選択できるようにしたい場合があります。

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
    public DoublePointNearEqualityComparerByTolerance(double tolerance) {  }
    
    public bool Equals(IDoublePoint a, IDoublePoint b)
    {
        return Math.Abs(a.X - b.X) <= tolerance  &&  Math.Abs(a.Y - b.Y) <= tolerance;
    }
    
}

上記のリンク先のページでは、このほぼ等しいかどうかのテストにいくつかの弱点があることを明記しています。これはIEqualityComparer<T>実装であるため、目的に対して十分でない場合は単純に交換できます。


6
正当なので、二点平等をテストするための提案比較方法は、壊れているIEqualityComparer<T>実装がなければならないであることを意味同値関係、実装全て 2つのオブジェクトが1/3に等しい比較する場合、それらは必要があり、互いに等しいが。上記とは逆の方法で実装IEquatable<T>またはクラス化されているクラスはすべてIEqualityComparer<T>壊れます。
スーパーキャット2013年

5
より良い例は文字列間の比較です。文字列が同じバイトシーケンスを含む場合にのみ文字列を等しいと見なす文字列比較メソッドを使用することは理にかなっていますが、大文字と小文字を区別しない比較など、同等関係を構成する他の便利な比較メソッドもあります。IEqualityComparer<String>「hello」を「Hello」と「hElLo」の両方に等しいと見なすaは、「Hello」と「hElLo」が互いに等しいと見なさなければならないことに注意してください。ただし、ほとんどの比較方法では問題にはなりません。
スーパーキャット2013年

@supercat、貴重なフィードバックをありがとう。今後数日で回答を修正する余裕がない可能性が高いので、必要に応じて、必要に応じて自由に編集してください。
stakx-2013年

これはまさに私が必要としていることであり、私がやっていることです。ただし、値が異なるとfalseを返すGetHashCodeをオーバーライドする必要があります。GetHashCodeをどのように処理しますか?
teapeng 2017年

@teapeng:あなたが話している特定のユースケースがわからない。一般的に言えば、GetHashCode2つの値が異なるかどうかをすばやく判断できるはずです。ルールはおおよそ次のとおりです。(1) GetHashCode同じ値に対して常に同じハッシュコードを生成する必要があります。(2) GetHashCodeは高速である必要があります(より速いEquals)。(3) GetHashCodeは正確である必要はありません(ほど正確ではありませんEquals)。つまり、異なる値に対して同じハッシュコードを生成する可能性があります。正確に作成できるほど優れていますが、高速に保つことがおそらくより重要です。
stakx-2017年

27

あなたはすでにそれらが何であるかの基本的な定義を持っています。簡単に言うと、IEquatable<T>class Tに実装Equalsする場合、型のオブジェクトのメソッドはT、オブジェクト自体(等しいかどうかがテストされている)が同じ型の別のインスタンスと等しいかどうかを通知しますT。一方、IEqualityComparer<T>は、の2つのインスタンスの等価性をテストするためのものでT、通常はのインスタンスの範囲外ですT

それらが何のためにあるかに関しては、最初は混乱する可能性があります。定義から、したがってIEquatable<T>(クラスT自体で定義されている)は、そのオブジェクト/インスタンスの一意性を表す事実上の標準であるべきであることは明らかです。HashSet<T>Dictionary<T, U>(考慮GetHashCodeもオーバーライドされます)、Containson List<T>などはこれを利用します。IEqualityComparer<T>onを実装しTても、上記の一般的なケースには役立ちません。その後、IEquatable<T>以外のクラスで実装する価値はほとんどありませんT。この:

class MyClass : IEquatable<T>

めったに意味がありません。

一方

class T : IEquatable<T>
{
    //override ==, !=, GetHashCode and non generic Equals as well

    public bool Equals(T other)
    {
        //....
    }
}

それがどのように行われるべきかです。

IEqualityComparer<T>等式のカスタム検証が必要な場合に役立ちますが、一般的なルールとしては役立ちません。たとえばPerson、ある時点のクラスで、年齢に基づいて2人の平等をテストする必要がある場合があります。その場合、次のことができます。

class Person
{
    public int Age;
}

class AgeEqualityTester : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Age.GetHashCode;
    }
}

それらをテストするには、

var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };

print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true

同様IEqualityComparer<T>T、意味がありません。

class Person : IEqualityComparer<Person>

確かにこれは機能しますが、見栄えが悪く、ロジックを無効にします。

通常、必要なのはIEquatable<T>です。また、理想的には1つだけ持つことができ、異なる基準に基づいてIEquatable<T>複数IEqualityComparer<T>が可能です。

IEqualityComparer<T>IEquatable<T>に正確に類似しているComparer<T>と、IComparable<T>その比較の目的のために使用されるのではなく等化されます。ここに私が同じ答えを書いた良いスレッド:)


public int GetHashCode(Person obj)戻るはずですobj.GetHashCode()
hyankov

@HristoYankovが返されobj.Age.GetHashCodeます。編集します。
nawfal 16

比較者が比較するオブジェクトのハッシュについて、そのような仮定をしなければならない理由を説明してください。あなたの比較者は、Personがそのハッシュをどのように計算するのか知りません、なぜそれを上書きするのですか?
hyankov 16

@HristoYankov 比較者は、Personがそのハッシュを計算する方法を知りません -確かに、それは私たちがperson.GetHashCodeどこにも直接呼び出していない理由です... なぜそれをオーバーライドしますか?-全体のポイントはIEqualityComparer、私たちのルール-私たちがよく知っているルール-に従って異なる比較実装を行うことなので、オーバーライドします。
nawfal 16

この例では、Ageプロパティに基づいて比較を行う必要があるため、を呼び出しますAge.GetHashCode。年齢はintタイプですが、.NETでのハッシュコードコントラクトのタイプが何であれ、そうですdifferent objects can have equal hash but equal objects cant ever have different hashes。これは私たちが盲目的に信じることができる契約です。もちろん、カスタムコンパレータもこのルールに従う必要があります。呼び出しがsomeObject.GetHashCode壊れることがある場合、それはタイプの実装者のsomeObject問題です。それは私たちの問題ではありません。
nawfal 16

11

IEqualityComparerは、2つのオブジェクトの等価性が外部で実装されている場合に使用します。たとえば、ソースがなかった2つの型の比較子を定義したい場合や、2つのものの等価性が限られたコンテキストでのみ意味がある場合などです。

IEquatableは、オブジェクト自体(同等性について比較されるオブジェクト)が実装するためのものです。


あなたは実際に「平等にプラグイン」(外部使用)問題を提起した唯一のものです。プラス1.
Royi Namir

4

1つは2つTのを比較します。もう一方は、他Tのと比較できます。通常、一度に使用する必要があるのは一度に1つだけで、両方は必要ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.