私はどこのシナリオを理解したいIEqualityComparer<T>
とIEquatable<T>
使用する必要があります。両方のMSDNドキュメントは非常によく似ています。
T
実装用のカスタムコレクションを作成する必要があることを示していますIEquatable<T>
。List<T>
そうでなければ、コレクションにある種の微妙なバグがあるでしょうか?
私はどこのシナリオを理解したいIEqualityComparer<T>
とIEquatable<T>
使用する必要があります。両方のMSDNドキュメントは非常によく似ています。
T
実装用のカスタムコレクションを作成する必要があることを示していますIEquatable<T>
。List<T>
そうでなければ、コレクションにある種の微妙なバグがあるでしょうか?
回答:
IEqualityComparer<T>
タイプの2つのオブジェクトの比較を実行するオブジェクトのインターフェースですT
。
IEquatable<T>
T
同じタイプの別のオブジェクトと比較できるように、タイプのオブジェクト用です。
IEqualityComparer<T>
、オブジェクトのためのインタフェースである(通常は異なる軽量クラスであるT
)上で動作する比較関数を提供するT
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>
実装であるため、目的に対して十分でない場合は単純に交換できます。
IEqualityComparer<T>
実装がなければならないであることを意味同値関係、実装全て 2つのオブジェクトが1/3に等しい比較する場合、それらは必要があり、互いに等しいが。上記とは逆の方法で実装IEquatable<T>
またはクラス化されているクラスはすべてIEqualityComparer<T>
壊れます。
IEqualityComparer<String>
「hello」を「Hello」と「hElLo」の両方に等しいと見なすaは、「Hello」と「hElLo」が互いに等しいと見なさなければならないことに注意してください。ただし、ほとんどの比較方法では問題にはなりません。
GetHashCode
2つの値が異なるかどうかをすばやく判断できるはずです。ルールはおおよそ次のとおりです。(1) GetHashCode
同じ値に対して常に同じハッシュコードを生成する必要があります。(2) GetHashCode
は高速である必要があります(より速いEquals
)。(3) GetHashCode
は正確である必要はありません(ほど正確ではありませんEquals
)。つまり、異なる値に対して同じハッシュコードを生成する可能性があります。正確に作成できるほど優れていますが、高速に保つことがおそらくより重要です。
あなたはすでにそれらが何であるかの基本的な定義を持っています。簡単に言うと、IEquatable<T>
class T
に実装Equals
する場合、型のオブジェクトのメソッドはT
、オブジェクト自体(等しいかどうかがテストされている)が同じ型の別のインスタンスと等しいかどうかを通知しますT
。一方、IEqualityComparer<T>
は、の2つのインスタンスの等価性をテストするためのものでT
、通常はのインスタンスの範囲外ですT
。
それらが何のためにあるかに関しては、最初は混乱する可能性があります。定義から、したがってIEquatable<T>
(クラスT
自体で定義されている)は、そのオブジェクト/インスタンスの一意性を表す事実上の標準であるべきであることは明らかです。HashSet<T>
、Dictionary<T, U>
(考慮GetHashCode
もオーバーライドされます)、Contains
on 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()
obj.Age.GetHashCode
ます。編集します。
person.GetHashCode
どこにも直接呼び出していない理由です... なぜそれをオーバーライドしますか?-全体のポイントはIEqualityComparer
、私たちのルール-私たちがよく知っているルール-に従って異なる比較実装を行うことなので、オーバーライドします。
Age
プロパティに基づいて比較を行う必要があるため、を呼び出しますAge.GetHashCode
。年齢はint
タイプですが、.NETでのハッシュコードコントラクトのタイプが何であれ、そうですdifferent objects can have equal hash but equal objects cant ever have different hashes
。これは私たちが盲目的に信じることができる契約です。もちろん、カスタムコンパレータもこのルールに従う必要があります。呼び出しがsomeObject.GetHashCode
壊れることがある場合、それはタイプの実装者のsomeObject
問題です。それは私たちの問題ではありません。
IEqualityComparerは、2つのオブジェクトの等価性が外部で実装されている場合に使用します。たとえば、ソースがなかった2つの型の比較子を定義したい場合や、2つのものの等価性が限られたコンテキストでのみ意味がある場合などです。
IEquatableは、オブジェクト自体(同等性について比較されるオブジェクト)が実装するためのものです。
EqualityComparer<T>
ので、「代わりのインタフェースを実装するEqualityComparer<T>
テストの平等を使用してIEquatable<T>